/* -*- Mode: C++; tab-width: 20; 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/. */ #include "WebGPUChild.h" #include "mozilla/EnumTypeTraits.h" #include "mozilla/dom/WebGPUBinding.h" #include "mozilla/dom/GPUUncapturedErrorEvent.h" #include "mozilla/webgpu/ValidationError.h" #include "mozilla/webgpu/ffi/wgpu.h" #include "Sampler.h" namespace mozilla { namespace webgpu { NS_IMPL_CYCLE_COLLECTION(WebGPUChild) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGPUChild, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGPUChild, Release) static ffi::WGPUCompareFunction ConvertCompareFunction( const dom::GPUCompareFunction& aCompare) { // Value of 0 = Undefined is reserved on the C side for "null" semantics. return ffi::WGPUCompareFunction(UnderlyingValue(aCompare) + 1); } static ffi::WGPUClient* initialize() { ffi::WGPUInfrastructure infra = ffi::wgpu_client_new(); return infra.client; } WebGPUChild::WebGPUChild() : mClient(initialize()), mIPCOpen(false) {} WebGPUChild::~WebGPUChild() { if (mClient) { ffi::wgpu_client_delete(mClient); } } RefPtr WebGPUChild::InstanceRequestAdapter( const dom::GPURequestAdapterOptions& aOptions) { const int max_ids = 10; RawId ids[max_ids] = {0}; unsigned long count = ffi::wgpu_client_make_adapter_ids(mClient, ids, max_ids); nsTArray sharedIds(count); for (unsigned long i = 0; i != count; ++i) { sharedIds.AppendElement(ids[i]); } return SendInstanceRequestAdapter(aOptions, sharedIds) ->Then( GetCurrentSerialEventTarget(), __func__, [](ipc::ByteBuf&& aInfoBuf) { // Ideally, we'd just send an empty ByteBuf, but the IPC code // complains if the capacity is zero... // So for the case where an adapter wasn't found, we just // transfer a single 0u64 in this buffer. return aInfoBuf.mLen > sizeof(uint64_t) ? AdapterPromise::CreateAndResolve(std::move(aInfoBuf), __func__) : AdapterPromise::CreateAndReject(Nothing(), __func__); }, [](const ipc::ResponseRejectReason& aReason) { return AdapterPromise::CreateAndReject(Some(aReason), __func__); }); } Maybe WebGPUChild::AdapterRequestDevice( RawId aSelfId, const dom::GPUDeviceDescriptor& aDesc) { RawId id = ffi::wgpu_client_make_device_id(mClient, aSelfId); ffi::WGPUDeviceDescriptor desc = {}; ffi::wgpu_client_fill_default_limits(&desc.limits); if (aDesc.mNonGuaranteedLimits.WasPassed()) { for (const auto& entry : aDesc.mNonGuaranteedLimits.Value().Entries()) { Unused << entry; // TODO } /*desc.limits.max_bind_groups = lim.mMaxBindGroups; desc.limits.max_dynamic_uniform_buffers_per_pipeline_layout = lim.mMaxDynamicUniformBuffersPerPipelineLayout; desc.limits.max_dynamic_storage_buffers_per_pipeline_layout = lim.mMaxDynamicStorageBuffersPerPipelineLayout; desc.limits.max_sampled_textures_per_shader_stage = lim.mMaxSampledTexturesPerShaderStage; desc.limits.max_samplers_per_shader_stage = lim.mMaxSamplersPerShaderStage; desc.limits.max_storage_buffers_per_shader_stage = lim.mMaxStorageBuffersPerShaderStage; desc.limits.max_storage_textures_per_shader_stage = lim.mMaxStorageTexturesPerShaderStage; desc.limits.max_uniform_buffers_per_shader_stage = lim.mMaxUniformBuffersPerShaderStage; desc.limits.max_uniform_buffer_binding_size = lim.mMaxUniformBufferBindingSize;*/ } ByteBuf bb; ffi::wgpu_client_serialize_device_descriptor(&desc, ToFFI(&bb)); if (SendAdapterRequestDevice(aSelfId, std::move(bb), id)) { return Some(id); } ffi::wgpu_client_kill_device_id(mClient, id); return Nothing(); } RawId WebGPUChild::DeviceCreateBuffer(RawId aSelfId, const dom::GPUBufferDescriptor& aDesc) { ffi::WGPUBufferDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } desc.size = aDesc.mSize; desc.usage = aDesc.mUsage; desc.mapped_at_creation = aDesc.mMappedAtCreation; ByteBuf bb; RawId id = ffi::wgpu_client_create_buffer(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::DeviceCreateTexture(RawId aSelfId, const dom::GPUTextureDescriptor& aDesc) { ffi::WGPUTextureDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } if (aDesc.mSize.IsRangeEnforcedUnsignedLongSequence()) { const auto& seq = aDesc.mSize.GetAsRangeEnforcedUnsignedLongSequence(); desc.size.width = seq.Length() > 0 ? seq[0] : 1; desc.size.height = seq.Length() > 1 ? seq[1] : 1; desc.size.depth_or_array_layers = seq.Length() > 2 ? seq[2] : 1; } else if (aDesc.mSize.IsGPUExtent3DDict()) { const auto& dict = aDesc.mSize.GetAsGPUExtent3DDict(); desc.size.width = dict.mWidth; desc.size.height = dict.mHeight; desc.size.depth_or_array_layers = dict.mDepthOrArrayLayers; } else { MOZ_CRASH("Unexpected union"); } desc.mip_level_count = aDesc.mMipLevelCount; desc.sample_count = aDesc.mSampleCount; desc.dimension = ffi::WGPUTextureDimension(aDesc.mDimension); desc.format = ffi::WGPUTextureFormat(aDesc.mFormat); desc.usage = aDesc.mUsage; ByteBuf bb; RawId id = ffi::wgpu_client_create_texture(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::TextureCreateView( RawId aSelfId, RawId aDeviceId, const dom::GPUTextureViewDescriptor& aDesc) { ffi::WGPUTextureViewDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } ffi::WGPUTextureFormat format = ffi::WGPUTextureFormat_Sentinel; if (aDesc.mFormat.WasPassed()) { format = ffi::WGPUTextureFormat(aDesc.mFormat.Value()); desc.format = &format; } ffi::WGPUTextureViewDimension dimension = ffi::WGPUTextureViewDimension_Sentinel; if (aDesc.mDimension.WasPassed()) { dimension = ffi::WGPUTextureViewDimension(aDesc.mDimension.Value()); desc.dimension = &dimension; } desc.aspect = ffi::WGPUTextureAspect(aDesc.mAspect); desc.base_mip_level = aDesc.mBaseMipLevel; desc.level_count = aDesc.mMipLevelCount.WasPassed() ? aDesc.mMipLevelCount.Value() : 0; desc.base_array_layer = aDesc.mBaseArrayLayer; desc.array_layer_count = aDesc.mArrayLayerCount.WasPassed() ? aDesc.mArrayLayerCount.Value() : 0; ByteBuf bb; RawId id = ffi::wgpu_client_create_texture_view(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendTextureAction(aSelfId, aDeviceId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::DeviceCreateSampler(RawId aSelfId, const dom::GPUSamplerDescriptor& aDesc) { ffi::WGPUSamplerDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } desc.address_modes[0] = ffi::WGPUAddressMode(aDesc.mAddressModeU); desc.address_modes[1] = ffi::WGPUAddressMode(aDesc.mAddressModeV); desc.address_modes[2] = ffi::WGPUAddressMode(aDesc.mAddressModeW); desc.mag_filter = ffi::WGPUFilterMode(aDesc.mMagFilter); desc.min_filter = ffi::WGPUFilterMode(aDesc.mMinFilter); desc.mipmap_filter = ffi::WGPUFilterMode(aDesc.mMipmapFilter); desc.lod_min_clamp = aDesc.mLodMinClamp; desc.lod_max_clamp = aDesc.mLodMaxClamp; ffi::WGPUCompareFunction comparison = ffi::WGPUCompareFunction_Sentinel; if (aDesc.mCompare.WasPassed()) { comparison = ConvertCompareFunction(aDesc.mCompare.Value()); desc.compare = &comparison; } ByteBuf bb; RawId id = ffi::wgpu_client_create_sampler(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::DeviceCreateCommandEncoder( RawId aSelfId, const dom::GPUCommandEncoderDescriptor& aDesc) { ffi::WGPUCommandEncoderDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } ByteBuf bb; RawId id = ffi::wgpu_client_create_command_encoder(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::CommandEncoderFinish( RawId aSelfId, RawId aDeviceId, const dom::GPUCommandBufferDescriptor& aDesc) { if (!SendCommandEncoderFinish(aSelfId, aDeviceId, aDesc)) { MOZ_CRASH("IPC failure"); } // We rely on knowledge that `CommandEncoderId` == `CommandBufferId` // TODO: refactor this to truly behave as if the encoder is being finished, // and a new command buffer ID is being created from it. Resolve the ID // type aliasing at the place that introduces it: `wgpu-core`. return aSelfId; } RawId WebGPUChild::DeviceCreateBindGroupLayout( RawId aSelfId, const dom::GPUBindGroupLayoutDescriptor& aDesc) { struct OptionalData { ffi::WGPUTextureViewDimension dim; ffi::WGPURawTextureSampleType type; ffi::WGPUTextureFormat format; }; nsTArray optional(aDesc.mEntries.Length()); for (const auto& entry : aDesc.mEntries) { OptionalData data = {}; if (entry.mTexture.WasPassed()) { const auto& texture = entry.mTexture.Value(); data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension); switch (texture.mSampleType) { case dom::GPUTextureSampleType::Float: data.type = ffi::WGPURawTextureSampleType_Float; break; case dom::GPUTextureSampleType::Unfilterable_float: data.type = ffi::WGPURawTextureSampleType_UnfilterableFloat; break; case dom::GPUTextureSampleType::Uint: data.type = ffi::WGPURawTextureSampleType_Uint; break; case dom::GPUTextureSampleType::Sint: data.type = ffi::WGPURawTextureSampleType_Sint; break; case dom::GPUTextureSampleType::Depth: data.type = ffi::WGPURawTextureSampleType_Depth; break; case dom::GPUTextureSampleType::EndGuard_: MOZ_ASSERT_UNREACHABLE(); } } if (entry.mStorageTexture.WasPassed()) { const auto& texture = entry.mStorageTexture.Value(); data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension); data.format = ffi::WGPUTextureFormat(texture.mFormat); } optional.AppendElement(data); } nsTArray entries(aDesc.mEntries.Length()); for (size_t i = 0; i < aDesc.mEntries.Length(); ++i) { const auto& entry = aDesc.mEntries[i]; ffi::WGPUBindGroupLayoutEntry e = {}; e.binding = entry.mBinding; e.visibility = entry.mVisibility; if (entry.mBuffer.WasPassed()) { switch (entry.mBuffer.Value().mType) { case dom::GPUBufferBindingType::Uniform: e.ty = ffi::WGPURawBindingType_UniformBuffer; break; case dom::GPUBufferBindingType::Storage: e.ty = ffi::WGPURawBindingType_StorageBuffer; break; case dom::GPUBufferBindingType::Read_only_storage: e.ty = ffi::WGPURawBindingType_ReadonlyStorageBuffer; break; case dom::GPUBufferBindingType::EndGuard_: MOZ_ASSERT_UNREACHABLE(); } e.has_dynamic_offset = entry.mBuffer.Value().mHasDynamicOffset; } if (entry.mTexture.WasPassed()) { e.ty = ffi::WGPURawBindingType_SampledTexture; e.view_dimension = &optional[i].dim; e.texture_sample_type = &optional[i].type; e.multisampled = entry.mTexture.Value().mMultisampled; } if (entry.mStorageTexture.WasPassed()) { e.ty = entry.mStorageTexture.Value().mAccess == dom::GPUStorageTextureAccess::Write_only ? ffi::WGPURawBindingType_WriteonlyStorageTexture : ffi::WGPURawBindingType_ReadonlyStorageTexture; e.view_dimension = &optional[i].dim; e.storage_texture_format = &optional[i].format; } if (entry.mSampler.WasPassed()) { e.ty = ffi::WGPURawBindingType_Sampler; switch (entry.mSampler.Value().mType) { case dom::GPUSamplerBindingType::Filtering: e.sampler_filter = true; break; case dom::GPUSamplerBindingType::Non_filtering: break; case dom::GPUSamplerBindingType::Comparison: e.sampler_compare = true; break; case dom::GPUSamplerBindingType::EndGuard_: MOZ_ASSERT_UNREACHABLE(); } } entries.AppendElement(e); } ffi::WGPUBindGroupLayoutDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } desc.entries = entries.Elements(); desc.entries_length = entries.Length(); ByteBuf bb; RawId id = ffi::wgpu_client_create_bind_group_layout(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::DeviceCreatePipelineLayout( RawId aSelfId, const dom::GPUPipelineLayoutDescriptor& aDesc) { nsTArray bindGroupLayouts( aDesc.mBindGroupLayouts.Length()); for (const auto& layout : aDesc.mBindGroupLayouts) { bindGroupLayouts.AppendElement(layout->mId); } ffi::WGPUPipelineLayoutDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } desc.bind_group_layouts = bindGroupLayouts.Elements(); desc.bind_group_layouts_length = bindGroupLayouts.Length(); ByteBuf bb; RawId id = ffi::wgpu_client_create_pipeline_layout(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::DeviceCreateBindGroup( RawId aSelfId, const dom::GPUBindGroupDescriptor& aDesc) { nsTArray entries(aDesc.mEntries.Length()); for (const auto& entry : aDesc.mEntries) { ffi::WGPUBindGroupEntry e = {}; e.binding = entry.mBinding; if (entry.mResource.IsGPUBufferBinding()) { const auto& bufBinding = entry.mResource.GetAsGPUBufferBinding(); e.buffer = bufBinding.mBuffer->mId; e.offset = bufBinding.mOffset; e.size = bufBinding.mSize.WasPassed() ? bufBinding.mSize.Value() : 0; } if (entry.mResource.IsGPUTextureView()) { e.texture_view = entry.mResource.GetAsGPUTextureView()->mId; } if (entry.mResource.IsGPUSampler()) { e.sampler = entry.mResource.GetAsGPUSampler()->mId; } entries.AppendElement(e); } ffi::WGPUBindGroupDescriptor desc = {}; nsCString label; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } desc.layout = aDesc.mLayout->mId; desc.entries = entries.Elements(); desc.entries_length = entries.Length(); ByteBuf bb; RawId id = ffi::wgpu_client_create_bind_group(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::DeviceCreateShaderModule( RawId aSelfId, const dom::GPUShaderModuleDescriptor& aDesc) { ffi::WGPUShaderModuleDescriptor desc = {}; nsCString wgsl; if (aDesc.mCode.IsUSVString()) { LossyCopyUTF16toASCII(aDesc.mCode.GetAsUSVString(), wgsl); desc.wgsl_chars = wgsl.get(); } else { const auto& code = aDesc.mCode.GetAsUint32Array(); code.ComputeState(); desc.spirv_words = code.Data(); desc.spirv_words_length = code.Length(); } ByteBuf bb; RawId id = ffi::wgpu_client_create_shader_module(mClient, aSelfId, &desc, ToFFI(&bb)); if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } RawId WebGPUChild::DeviceCreateComputePipeline( RawId aSelfId, const dom::GPUComputePipelineDescriptor& aDesc, nsTArray* const aImplicitBindGroupLayoutIds) { ffi::WGPUComputePipelineDescriptor desc = {}; nsCString label, entryPoint; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } if (aDesc.mLayout.WasPassed()) { desc.layout = aDesc.mLayout.Value().mId; } desc.stage.module = aDesc.mCompute.mModule->mId; LossyCopyUTF16toASCII(aDesc.mCompute.mEntryPoint, entryPoint); desc.stage.entry_point = entryPoint.get(); ByteBuf bb; RawId implicit_bgl_ids[WGPUMAX_BIND_GROUPS] = {}; RawId id = ffi::wgpu_client_create_compute_pipeline( mClient, aSelfId, &desc, ToFFI(&bb), implicit_bgl_ids); for (const auto& cur : implicit_bgl_ids) { if (!cur) break; aImplicitBindGroupLayoutIds->AppendElement(cur); } if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } static ffi::WGPUMultisampleState ConvertMultisampleState( const dom::GPUMultisampleState& aDesc) { ffi::WGPUMultisampleState desc = {}; desc.count = aDesc.mCount; desc.mask = aDesc.mMask; desc.alpha_to_coverage_enabled = aDesc.mAlphaToCoverageEnabled; return desc; } static ffi::WGPUBlendComponent ConvertBlendComponent( const dom::GPUBlendComponent& aDesc) { ffi::WGPUBlendComponent desc = {}; desc.src_factor = ffi::WGPUBlendFactor(aDesc.mSrcFactor); desc.dst_factor = ffi::WGPUBlendFactor(aDesc.mDstFactor); desc.operation = ffi::WGPUBlendOperation(aDesc.mOperation); return desc; } static ffi::WGPUStencilFaceState ConvertStencilFaceState( const dom::GPUStencilFaceState& aDesc) { ffi::WGPUStencilFaceState desc = {}; desc.compare = ConvertCompareFunction(aDesc.mCompare); desc.fail_op = ffi::WGPUStencilOperation(aDesc.mFailOp); desc.depth_fail_op = ffi::WGPUStencilOperation(aDesc.mDepthFailOp); desc.pass_op = ffi::WGPUStencilOperation(aDesc.mPassOp); return desc; } static ffi::WGPUDepthStencilState ConvertDepthStencilState( const dom::GPUDepthStencilState& aDesc) { ffi::WGPUDepthStencilState desc = {}; desc.format = ffi::WGPUTextureFormat(aDesc.mFormat); desc.depth_write_enabled = aDesc.mDepthWriteEnabled; desc.depth_compare = ConvertCompareFunction(aDesc.mDepthCompare); desc.stencil.front = ConvertStencilFaceState(aDesc.mStencilFront); desc.stencil.back = ConvertStencilFaceState(aDesc.mStencilBack); desc.stencil.read_mask = aDesc.mStencilReadMask; desc.stencil.write_mask = aDesc.mStencilWriteMask; desc.bias.constant = aDesc.mDepthBias; desc.bias.slope_scale = aDesc.mDepthBiasSlopeScale; desc.bias.clamp = aDesc.mDepthBiasClamp; return desc; } RawId WebGPUChild::DeviceCreateRenderPipeline( RawId aSelfId, const dom::GPURenderPipelineDescriptor& aDesc, nsTArray* const aImplicitBindGroupLayoutIds) { // A bunch of stack locals that we can have pointers into nsTArray vertexBuffers; nsTArray vertexAttributes; ffi::WGPURenderPipelineDescriptor desc = {}; nsCString label, vsEntry, fsEntry; ffi::WGPUIndexFormat stripIndexFormat = ffi::WGPUIndexFormat_Uint16; ffi::WGPUFace cullFace = ffi::WGPUFace_Front; ffi::WGPUVertexState vertexState = {}; ffi::WGPUFragmentState fragmentState = {}; nsTArray colorStates; nsTArray blendStates; if (aDesc.mLabel.WasPassed()) { LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label); desc.label = label.get(); } if (aDesc.mLayout.WasPassed()) { desc.layout = aDesc.mLayout.Value().mId; } { const auto& stage = aDesc.mVertex; vertexState.stage.module = stage.mModule->mId; LossyCopyUTF16toASCII(stage.mEntryPoint, vsEntry); vertexState.stage.entry_point = vsEntry.get(); for (const auto& vertex_desc : stage.mBuffers) { ffi::WGPUVertexBufferLayout vb_desc = {}; if (!vertex_desc.IsNull()) { const auto& vd = vertex_desc.Value(); vb_desc.array_stride = vd.mArrayStride; vb_desc.step_mode = ffi::WGPUInputStepMode(vd.mStepMode); // Note: we are setting the length but not the pointer vb_desc.attributes_length = vd.mAttributes.Length(); for (const auto& vat : vd.mAttributes) { ffi::WGPUVertexAttribute ad = {}; ad.offset = vat.mOffset; ad.format = ffi::WGPUVertexFormat(vat.mFormat); ad.shader_location = vat.mShaderLocation; vertexAttributes.AppendElement(ad); } } vertexBuffers.AppendElement(vb_desc); } // Now patch up all the pointers to attribute lists. size_t numAttributes = 0; for (auto& vb_desc : vertexBuffers) { vb_desc.attributes = vertexAttributes.Elements() + numAttributes; numAttributes += vb_desc.attributes_length; } vertexState.buffers = vertexBuffers.Elements(); vertexState.buffers_length = vertexBuffers.Length(); desc.vertex = &vertexState; } if (aDesc.mFragment.WasPassed()) { const auto& stage = aDesc.mFragment.Value(); fragmentState.stage.module = stage.mModule->mId; LossyCopyUTF16toASCII(stage.mEntryPoint, fsEntry); fragmentState.stage.entry_point = fsEntry.get(); // Note: we pre-collect the blend states into a different array // so that we can have non-stale pointers into it. for (const auto& colorState : stage.mTargets) { ffi::WGPUColorTargetState desc = {}; desc.format = ffi::WGPUTextureFormat(colorState.mFormat); desc.write_mask = colorState.mWriteMask; colorStates.AppendElement(desc); ffi::WGPUBlendState bs = {}; if (colorState.mBlend.WasPassed()) { const auto& blend = colorState.mBlend.Value(); bs.alpha = ConvertBlendComponent(blend.mAlpha); bs.color = ConvertBlendComponent(blend.mColor); } blendStates.AppendElement(bs); } for (size_t i = 0; i < colorStates.Length(); ++i) { if (stage.mTargets[i].mBlend.WasPassed()) { colorStates[i].blend = &blendStates[i]; } } fragmentState.targets = colorStates.Elements(); fragmentState.targets_length = colorStates.Length(); desc.fragment = &fragmentState; } { const auto& prim = aDesc.mPrimitive; desc.primitive.topology = ffi::WGPUPrimitiveTopology(prim.mTopology); if (prim.mStripIndexFormat.WasPassed()) { stripIndexFormat = ffi::WGPUIndexFormat(prim.mStripIndexFormat.Value()); desc.primitive.strip_index_format = &stripIndexFormat; } desc.primitive.front_face = ffi::WGPUFrontFace(prim.mFrontFace); if (prim.mCullMode != dom::GPUCullMode::None) { cullFace = prim.mCullMode == dom::GPUCullMode::Front ? ffi::WGPUFace_Front : ffi::WGPUFace_Back; desc.primitive.cull_mode = &cullFace; } } desc.multisample = ConvertMultisampleState(aDesc.mMultisample); ffi::WGPUDepthStencilState depthStencilState = {}; if (aDesc.mDepthStencil.WasPassed()) { depthStencilState = ConvertDepthStencilState(aDesc.mDepthStencil.Value()); desc.depth_stencil = &depthStencilState; } ByteBuf bb; RawId implicit_bgl_ids[WGPUMAX_BIND_GROUPS] = {}; RawId id = ffi::wgpu_client_create_render_pipeline( mClient, aSelfId, &desc, ToFFI(&bb), implicit_bgl_ids); for (const auto& cur : implicit_bgl_ids) { if (!cur) break; aImplicitBindGroupLayoutIds->AppendElement(cur); } if (!SendDeviceAction(aSelfId, std::move(bb))) { MOZ_CRASH("IPC failure"); } return id; } ipc::IPCResult WebGPUChild::RecvError(RawId aDeviceId, const nsACString& aMessage) { if (!aDeviceId) { // TODO: figure out how to report these kinds of errors printf_stderr("Validation error without device target: %s\n", PromiseFlatCString(aMessage).get()); } else if (mDeviceMap.find(aDeviceId) == mDeviceMap.end()) { printf_stderr("Validation error on a dropped device: %s\n", PromiseFlatCString(aMessage).get()); } else { auto* target = mDeviceMap[aDeviceId]; MOZ_ASSERT(target); dom::GPUUncapturedErrorEventInit init; init.mError.SetAsGPUValidationError() = new ValidationError(target, aMessage); RefPtr event = dom::GPUUncapturedErrorEvent::Constructor(target, u"uncapturederror"_ns, init); target->DispatchEvent(*event); } return IPC_OK(); } ipc::IPCResult WebGPUChild::RecvDropAction(const ipc::ByteBuf& aByteBuf) { const auto* byteBuf = ToFFI(&aByteBuf); ffi::wgpu_client_drop_action(mClient, byteBuf); return IPC_OK(); } void WebGPUChild::DeviceCreateSwapChain(RawId aSelfId, const RGBDescriptor& aRgbDesc, size_t maxBufferCount, wr::ExternalImageId aExternalImageId) { RawId queueId = aSelfId; // TODO: multiple queues nsTArray bufferIds(maxBufferCount); for (size_t i = 0; i < maxBufferCount; ++i) { bufferIds.AppendElement(ffi::wgpu_client_make_buffer_id(mClient, aSelfId)); } SendDeviceCreateSwapChain(aSelfId, queueId, aRgbDesc, bufferIds, aExternalImageId); } void WebGPUChild::SwapChainPresent(wr::ExternalImageId aExternalImageId, RawId aTextureId) { // Hack: the function expects `DeviceId`, but it only uses it for `backend()` // selection. RawId encoderId = ffi::wgpu_client_make_encoder_id(mClient, aTextureId); SendSwapChainPresent(aExternalImageId, aTextureId, encoderId); } void WebGPUChild::RegisterDevice(RawId aId, Device* aDevice) { mDeviceMap.insert({aId, aDevice}); } void WebGPUChild::UnregisterDevice(RawId aId) { mDeviceMap.erase(aId); SendDeviceDestroy(aId); } } // namespace webgpu } // namespace mozilla