Device.cpp (36744B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "Device.h" 7 8 #include "Adapter.h" 9 #include "BindGroup.h" 10 #include "Buffer.h" 11 #include "CommandEncoder.h" 12 #include "CompilationInfo.h" 13 #include "ComputePipeline.h" 14 #include "DeviceLostInfo.h" 15 #include "ExternalTexture.h" 16 #include "InternalError.h" 17 #include "OutOfMemoryError.h" 18 #include "PipelineLayout.h" 19 #include "QuerySet.h" 20 #include "Queue.h" 21 #include "RenderBundleEncoder.h" 22 #include "RenderPipeline.h" 23 #include "Sampler.h" 24 #include "SupportedFeatures.h" 25 #include "SupportedLimits.h" 26 #include "Texture.h" 27 #include "TextureView.h" 28 #include "Utility.h" 29 #include "ValidationError.h" 30 #include "ipc/WebGPUChild.h" 31 #include "js/ArrayBuffer.h" 32 #include "js/Value.h" 33 #include "mozilla/Attributes.h" 34 #include "mozilla/ErrorResult.h" 35 #include "mozilla/Logging.h" 36 #include "mozilla/RefPtr.h" 37 #include "mozilla/dom/Console.h" 38 #include "mozilla/dom/Promise.h" 39 #include "mozilla/dom/VideoFrame.h" 40 #include "mozilla/dom/WebGPUBinding.h" 41 #include "mozilla/gfx/gfxVars.h" 42 #include "nsGlobalWindowInner.h" 43 44 namespace mozilla::webgpu { 45 46 mozilla::LazyLogModule gWebGPULog("WebGPU"); 47 48 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(Device, DOMEventTargetHelper, 49 mQueue, mFeatures, mLimits, 50 mAdapterInfo, mLostPromise); 51 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(Device, DOMEventTargetHelper) 52 GPU_IMPL_JS_WRAP(Device) 53 54 /* static */ CheckedInt<uint32_t> Device::BufferStrideWithMask( 55 const gfx::IntSize& aSize, const gfx::SurfaceFormat& aFormat) { 56 constexpr uint32_t kBufferAlignmentMask = 0xff; 57 return CheckedInt<uint32_t>(aSize.width) * gfx::BytesPerPixel(aFormat) + 58 kBufferAlignmentMask; 59 } 60 61 Device::Device(Adapter* const aParent, RawId aDeviceId, RawId aQueueId, 62 RefPtr<SupportedFeatures> aFeatures, 63 RefPtr<SupportedLimits> aLimits, 64 RefPtr<webgpu::AdapterInfo> aAdapterInfo, 65 RefPtr<dom::Promise> aLostPromise) 66 : DOMEventTargetHelper(aParent->GetParentObject()), 67 ObjectBase(aParent->GetChild(), aDeviceId, ffi::wgpu_client_drop_device), 68 mFeatures(std::move(aFeatures)), 69 mLimits(std::move(aLimits)), 70 mAdapterInfo(std::move(aAdapterInfo)), 71 mSupportSharedTextureInSwapChain( 72 aParent->SupportSharedTextureInSwapChain()), 73 mLostPromise(std::move(aLostPromise)), 74 mQueue(new class Queue(this, aQueueId)) { 75 GetChild()->RegisterDevice(this); 76 KeepAliveIfHasListenersFor(nsGkAtoms::onuncapturederror); 77 } 78 79 Device::~Device() { GetChild()->UnregisterDevice(GetId()); } 80 81 void Device::TrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Insert(aBuffer); } 82 83 void Device::UntrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Remove(aBuffer); } 84 85 dom::Promise* Device::GetLost(ErrorResult& aRv) { 86 aRv = NS_OK; 87 return mLostPromise; 88 } 89 90 void Device::ResolveLost(dom::GPUDeviceLostReason aReason, 91 const nsAString& aMessage) { 92 if (mLostPromise->State() != dom::Promise::PromiseState::Pending) { 93 // The lost promise was already resolved or rejected. 94 return; 95 } 96 RefPtr<DeviceLostInfo> info = 97 MakeRefPtr<DeviceLostInfo>(GetParentObject(), aReason, aMessage); 98 mLostPromise->MaybeResolve(info); 99 } 100 101 already_AddRefed<Buffer> Device::CreateBuffer( 102 const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) { 103 return Buffer::Create(this, GetId(), aDesc, aRv); 104 } 105 106 already_AddRefed<Texture> Device::CreateTextureForSwapChain( 107 const dom::GPUCanvasConfiguration* const aConfig, 108 const gfx::IntSize& aCanvasSize, layers::RemoteTextureOwnerId aOwnerId) { 109 MOZ_ASSERT(aConfig); 110 111 dom::GPUTextureDescriptor desc; 112 desc.mDimension = dom::GPUTextureDimension::_2d; 113 auto& sizeDict = desc.mSize.SetAsGPUExtent3DDict(); 114 sizeDict.mWidth = aCanvasSize.width; 115 sizeDict.mHeight = aCanvasSize.height; 116 sizeDict.mDepthOrArrayLayers = 1; 117 desc.mFormat = aConfig->mFormat; 118 desc.mMipLevelCount = 1; 119 desc.mSampleCount = 1; 120 desc.mUsage = aConfig->mUsage | dom::GPUTextureUsage_Binding::COPY_SRC; 121 desc.mViewFormats = aConfig->mViewFormats; 122 123 return CreateTexture(desc, Some(aOwnerId)); 124 } 125 126 already_AddRefed<Texture> Device::CreateTexture( 127 const dom::GPUTextureDescriptor& aDesc) { 128 return CreateTexture(aDesc, /* aOwnerId */ Nothing()); 129 } 130 131 already_AddRefed<Texture> Device::CreateTexture( 132 const dom::GPUTextureDescriptor& aDesc, 133 Maybe<layers::RemoteTextureOwnerId> aOwnerId) { 134 ffi::WGPUTextureDescriptor desc = {}; 135 136 webgpu::StringHelper label(aDesc.mLabel); 137 desc.label = label.Get(); 138 139 if (aDesc.mSize.IsRangeEnforcedUnsignedLongSequence()) { 140 const auto& seq = aDesc.mSize.GetAsRangeEnforcedUnsignedLongSequence(); 141 desc.size.width = seq.Length() > 0 ? seq[0] : 1; 142 desc.size.height = seq.Length() > 1 ? seq[1] : 1; 143 desc.size.depth_or_array_layers = seq.Length() > 2 ? seq[2] : 1; 144 } else if (aDesc.mSize.IsGPUExtent3DDict()) { 145 const auto& dict = aDesc.mSize.GetAsGPUExtent3DDict(); 146 desc.size.width = dict.mWidth; 147 desc.size.height = dict.mHeight; 148 desc.size.depth_or_array_layers = dict.mDepthOrArrayLayers; 149 } else { 150 MOZ_CRASH("Unexpected union"); 151 } 152 desc.mip_level_count = aDesc.mMipLevelCount; 153 desc.sample_count = aDesc.mSampleCount; 154 desc.dimension = ffi::WGPUTextureDimension(aDesc.mDimension); 155 desc.format = ConvertTextureFormat(aDesc.mFormat); 156 desc.usage = aDesc.mUsage; 157 158 AutoTArray<ffi::WGPUTextureFormat, 8> viewFormats; 159 for (auto format : aDesc.mViewFormats) { 160 viewFormats.AppendElement(ConvertTextureFormat(format)); 161 } 162 desc.view_formats = {viewFormats.Elements(), viewFormats.Length()}; 163 164 Maybe<ffi::WGPUSwapChainId> ownerId; 165 if (aOwnerId.isSome()) { 166 ownerId = Some(ffi::WGPUSwapChainId{aOwnerId->mId}); 167 } 168 169 RawId id = ffi::wgpu_client_create_texture(GetClient(), GetId(), &desc, 170 ownerId.ptrOr(nullptr)); 171 172 RefPtr<Texture> texture = new Texture(this, id, aDesc); 173 texture->SetLabel(aDesc.mLabel); 174 return texture.forget(); 175 } 176 177 already_AddRefed<ExternalTexture> Device::ImportExternalTexture( 178 const dom::GPUExternalTextureDescriptor& aDesc, ErrorResult& aRv) { 179 if (!gfx::gfxVars::AllowWebGPUExternalTexture()) { 180 aRv.ThrowNotSupportedError("WebGPU external textures are disabled"); 181 return nullptr; 182 } 183 184 RefPtr<ExternalTexture> externalTexture = 185 mExternalTextureCache.GetOrCreate(this, aDesc, aRv); 186 187 switch (aDesc.mSource.GetType()) { 188 case dom::OwningHTMLVideoElementOrVideoFrame::Type::eHTMLVideoElement: { 189 // Add the texture to the list of textures to be expired in the next 190 // automatic expiry task, scheduling the task if required. 191 // Using RunInStableState ensures it runs after any microtasks that may 192 // be scheduled during the current task. 193 if (mExternalTexturesToExpire.IsEmpty()) { 194 nsContentUtils::RunInStableState( 195 NewRunnableMethod("webgpu::Device::ExpireExternalTextures", this, 196 &Device::ExpireExternalTextures)); 197 } 198 mExternalTexturesToExpire.AppendElement(externalTexture); 199 } break; 200 case dom::OwningHTMLVideoElementOrVideoFrame::Type::eVideoFrame: { 201 // Ensure the VideoFrame knows about the external texture, so that it can 202 // expire it when the VideoFrame is closed. 203 const auto& videoFrame = aDesc.mSource.GetAsVideoFrame(); 204 videoFrame->TrackWebGPUExternalTexture(externalTexture.get()); 205 } break; 206 } 207 208 return externalTexture.forget(); 209 } 210 211 void Device::ExpireExternalTextures() { 212 MOZ_ASSERT(!mExternalTexturesToExpire.IsEmpty(), 213 "Task should not have been scheduled if there are no external " 214 "textures to expire"); 215 for (const auto& weakExternalTexture : mExternalTexturesToExpire) { 216 if (auto* externalTexture = weakExternalTexture.get()) { 217 externalTexture->Expire(); 218 } 219 } 220 mExternalTexturesToExpire.Clear(); 221 } 222 223 already_AddRefed<Sampler> Device::CreateSampler( 224 const dom::GPUSamplerDescriptor& aDesc) { 225 ffi::WGPUSamplerDescriptor desc = {}; 226 webgpu::StringHelper label(aDesc.mLabel); 227 228 desc.label = label.Get(); 229 desc.address_modes[0] = ffi::WGPUAddressMode(aDesc.mAddressModeU); 230 desc.address_modes[1] = ffi::WGPUAddressMode(aDesc.mAddressModeV); 231 desc.address_modes[2] = ffi::WGPUAddressMode(aDesc.mAddressModeW); 232 desc.mag_filter = ffi::WGPUFilterMode(aDesc.mMagFilter); 233 desc.min_filter = ffi::WGPUFilterMode(aDesc.mMinFilter); 234 desc.mipmap_filter = ffi::WGPUMipmapFilterMode(aDesc.mMipmapFilter); 235 desc.lod_min_clamp = aDesc.mLodMinClamp; 236 desc.lod_max_clamp = aDesc.mLodMaxClamp; 237 desc.max_anisotropy = aDesc.mMaxAnisotropy; 238 239 ffi::WGPUCompareFunction comparison = ffi::WGPUCompareFunction_Sentinel; 240 if (aDesc.mCompare.WasPassed()) { 241 comparison = ConvertCompareFunction(aDesc.mCompare.Value()); 242 desc.compare = &comparison; 243 } 244 245 RawId id = ffi::wgpu_client_create_sampler(GetClient(), GetId(), &desc); 246 247 RefPtr<Sampler> sampler = new Sampler(this, id); 248 sampler->SetLabel(aDesc.mLabel); 249 return sampler.forget(); 250 } 251 252 already_AddRefed<CommandEncoder> Device::CreateCommandEncoder( 253 const dom::GPUCommandEncoderDescriptor& aDesc) { 254 ffi::WGPUCommandEncoderDescriptor desc = {}; 255 256 webgpu::StringHelper label(aDesc.mLabel); 257 desc.label = label.Get(); 258 259 RawId id = 260 ffi::wgpu_client_create_command_encoder(GetClient(), GetId(), &desc); 261 262 RefPtr<CommandEncoder> encoder = new CommandEncoder(this, id); 263 encoder->SetLabel(aDesc.mLabel); 264 return encoder.forget(); 265 } 266 267 already_AddRefed<RenderBundleEncoder> Device::CreateRenderBundleEncoder( 268 const dom::GPURenderBundleEncoderDescriptor& aDesc) { 269 auto id = ffi::wgpu_client_make_render_bundle_encoder_id(GetClient()); 270 RefPtr<RenderBundleEncoder> encoder = 271 new RenderBundleEncoder(this, id, aDesc); 272 encoder->SetLabel(aDesc.mLabel); 273 return encoder.forget(); 274 } 275 276 already_AddRefed<QuerySet> Device::CreateQuerySet( 277 const dom::GPUQuerySetDescriptor& aDesc, ErrorResult& aRv) { 278 ffi::WGPURawQuerySetDescriptor desc = {}; 279 280 webgpu::StringHelper label(aDesc.mLabel); 281 desc.label = label.Get(); 282 ffi::WGPURawQueryType type; 283 switch (aDesc.mType) { 284 case dom::GPUQueryType::Occlusion: 285 type = ffi::WGPURawQueryType_Occlusion; 286 break; 287 case dom::GPUQueryType::Timestamp: 288 type = ffi::WGPURawQueryType_Timestamp; 289 if (!mFeatures->Features().count(dom::GPUFeatureName::Timestamp_query)) { 290 aRv.ThrowTypeError( 291 "requested query set of type `timestamp`, but the " 292 "`timestamp-query` feature is not enabled on the device"); 293 return nullptr; 294 } 295 break; 296 }; 297 desc.ty = type; 298 desc.count = aDesc.mCount; 299 300 RawId id = ffi::wgpu_client_create_query_set(GetClient(), GetId(), &desc); 301 302 RefPtr<QuerySet> querySet = new QuerySet(this, aDesc, id); 303 querySet->SetLabel(aDesc.mLabel); 304 return querySet.forget(); 305 } 306 307 already_AddRefed<BindGroupLayout> Device::CreateBindGroupLayout( 308 const dom::GPUBindGroupLayoutDescriptor& aDesc) { 309 struct OptionalData { 310 ffi::WGPUTextureViewDimension dim; 311 ffi::WGPURawTextureSampleType type; 312 ffi::WGPUTextureFormat format; 313 }; 314 nsTArray<OptionalData> optional(aDesc.mEntries.Length()); 315 for (const auto& entry : aDesc.mEntries) { 316 OptionalData data = {}; 317 if (entry.mTexture.WasPassed()) { 318 const auto& texture = entry.mTexture.Value(); 319 data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension); 320 switch (texture.mSampleType) { 321 case dom::GPUTextureSampleType::Float: 322 data.type = ffi::WGPURawTextureSampleType_Float; 323 break; 324 case dom::GPUTextureSampleType::Unfilterable_float: 325 data.type = ffi::WGPURawTextureSampleType_UnfilterableFloat; 326 break; 327 case dom::GPUTextureSampleType::Uint: 328 data.type = ffi::WGPURawTextureSampleType_Uint; 329 break; 330 case dom::GPUTextureSampleType::Sint: 331 data.type = ffi::WGPURawTextureSampleType_Sint; 332 break; 333 case dom::GPUTextureSampleType::Depth: 334 data.type = ffi::WGPURawTextureSampleType_Depth; 335 break; 336 } 337 } 338 if (entry.mStorageTexture.WasPassed()) { 339 const auto& texture = entry.mStorageTexture.Value(); 340 data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension); 341 data.format = ConvertTextureFormat(texture.mFormat); 342 } 343 optional.AppendElement(data); 344 } 345 346 nsTArray<ffi::WGPUBindGroupLayoutEntry> entries(aDesc.mEntries.Length()); 347 for (size_t i = 0; i < aDesc.mEntries.Length(); ++i) { 348 const auto& entry = aDesc.mEntries[i]; 349 ffi::WGPUBindGroupLayoutEntry e = {}; 350 e.binding = entry.mBinding; 351 e.visibility = entry.mVisibility; 352 if (entry.mBuffer.WasPassed()) { 353 switch (entry.mBuffer.Value().mType) { 354 case dom::GPUBufferBindingType::Uniform: 355 e.ty = ffi::WGPURawBindingType_UniformBuffer; 356 break; 357 case dom::GPUBufferBindingType::Storage: 358 e.ty = ffi::WGPURawBindingType_StorageBuffer; 359 break; 360 case dom::GPUBufferBindingType::Read_only_storage: 361 e.ty = ffi::WGPURawBindingType_ReadonlyStorageBuffer; 362 break; 363 } 364 e.has_dynamic_offset = entry.mBuffer.Value().mHasDynamicOffset; 365 e.min_binding_size = entry.mBuffer.Value().mMinBindingSize; 366 } 367 if (entry.mTexture.WasPassed()) { 368 e.ty = ffi::WGPURawBindingType_SampledTexture; 369 e.view_dimension = &optional[i].dim; 370 e.texture_sample_type = &optional[i].type; 371 e.multisampled = entry.mTexture.Value().mMultisampled; 372 } 373 if (entry.mStorageTexture.WasPassed()) { 374 switch (entry.mStorageTexture.Value().mAccess) { 375 case dom::GPUStorageTextureAccess::Write_only: { 376 e.ty = ffi::WGPURawBindingType_WriteonlyStorageTexture; 377 break; 378 } 379 case dom::GPUStorageTextureAccess::Read_only: { 380 e.ty = ffi::WGPURawBindingType_ReadonlyStorageTexture; 381 break; 382 } 383 case dom::GPUStorageTextureAccess::Read_write: { 384 e.ty = ffi::WGPURawBindingType_ReadWriteStorageTexture; 385 break; 386 } 387 default: { 388 MOZ_ASSERT_UNREACHABLE(); 389 } 390 } 391 e.view_dimension = &optional[i].dim; 392 e.storage_texture_format = &optional[i].format; 393 } 394 if (entry.mSampler.WasPassed()) { 395 e.ty = ffi::WGPURawBindingType_Sampler; 396 switch (entry.mSampler.Value().mType) { 397 case dom::GPUSamplerBindingType::Filtering: 398 e.sampler_filter = true; 399 break; 400 case dom::GPUSamplerBindingType::Non_filtering: 401 break; 402 case dom::GPUSamplerBindingType::Comparison: 403 e.sampler_compare = true; 404 break; 405 } 406 } 407 if (entry.mExternalTexture.WasPassed()) { 408 e.ty = ffi::WGPURawBindingType_ExternalTexture; 409 } 410 entries.AppendElement(e); 411 } 412 413 ffi::WGPUBindGroupLayoutDescriptor desc = {}; 414 415 webgpu::StringHelper label(aDesc.mLabel); 416 desc.label = label.Get(); 417 desc.entries = {entries.Elements(), entries.Length()}; 418 419 RawId id = 420 ffi::wgpu_client_create_bind_group_layout(GetClient(), GetId(), &desc); 421 422 RefPtr<BindGroupLayout> object = new BindGroupLayout(this, id); 423 object->SetLabel(aDesc.mLabel); 424 return object.forget(); 425 } 426 427 already_AddRefed<PipelineLayout> Device::CreatePipelineLayout( 428 const dom::GPUPipelineLayoutDescriptor& aDesc) { 429 nsTArray<ffi::WGPUBindGroupLayoutId> bindGroupLayouts( 430 aDesc.mBindGroupLayouts.Length()); 431 432 for (const auto& layout : aDesc.mBindGroupLayouts) { 433 bindGroupLayouts.AppendElement(layout->GetId()); 434 } 435 436 ffi::WGPUPipelineLayoutDescriptor desc = {}; 437 438 webgpu::StringHelper label(aDesc.mLabel); 439 desc.label = label.Get(); 440 desc.bind_group_layouts = {bindGroupLayouts.Elements(), 441 bindGroupLayouts.Length()}; 442 443 RawId id = 444 ffi::wgpu_client_create_pipeline_layout(GetClient(), GetId(), &desc); 445 446 RefPtr<PipelineLayout> object = new PipelineLayout(this, id); 447 object->SetLabel(aDesc.mLabel); 448 return object.forget(); 449 } 450 451 already_AddRefed<BindGroup> Device::CreateBindGroup( 452 const dom::GPUBindGroupDescriptor& aDesc) { 453 nsTArray<ffi::WGPUBindGroupEntry> entries(aDesc.mEntries.Length()); 454 CanvasContextArray canvasContexts; 455 nsTArray<RefPtr<ExternalTexture>> externalTextures; 456 for (const auto& entry : aDesc.mEntries) { 457 ffi::WGPUBindGroupEntry e = {}; 458 e.binding = entry.mBinding; 459 auto setTextureViewBinding = 460 [&e, &canvasContexts](const TextureView& texture_view) { 461 e.texture_view = texture_view.GetId(); 462 auto context = texture_view.GetTargetContext(); 463 if (context) { 464 canvasContexts.AppendElement(context); 465 } 466 }; 467 if (entry.mResource.IsGPUBuffer()) { 468 const auto& buffer = entry.mResource.GetAsGPUBuffer(); 469 if (!buffer->GetId()) { 470 NS_WARNING("Buffer has no id -- ignoring."); 471 continue; 472 } 473 e.buffer = buffer->GetId(); 474 e.offset = 0; 475 e.size_passed = false; 476 e.size = 0; 477 } else if (entry.mResource.IsGPUBufferBinding()) { 478 const auto& bufBinding = entry.mResource.GetAsGPUBufferBinding(); 479 if (!bufBinding.mBuffer->GetId()) { 480 NS_WARNING("Buffer binding has no id -- ignoring."); 481 continue; 482 } 483 e.buffer = bufBinding.mBuffer->GetId(); 484 e.offset = bufBinding.mOffset; 485 e.size_passed = bufBinding.mSize.WasPassed(); 486 if (e.size_passed) { 487 e.size = bufBinding.mSize.Value(); 488 } else { 489 e.size = 0; 490 } 491 } else if (entry.mResource.IsGPUTexture()) { 492 auto texture = entry.mResource.GetAsGPUTexture(); 493 const dom::GPUTextureViewDescriptor defaultDesc{}; 494 RefPtr<TextureView> texture_view = texture->CreateView(defaultDesc); 495 setTextureViewBinding(*texture_view); 496 } else if (entry.mResource.IsGPUTextureView()) { 497 auto texture_view = entry.mResource.GetAsGPUTextureView(); 498 setTextureViewBinding(texture_view); 499 } else if (entry.mResource.IsGPUSampler()) { 500 e.sampler = entry.mResource.GetAsGPUSampler()->GetId(); 501 } else if (entry.mResource.IsGPUExternalTexture()) { 502 const RefPtr<ExternalTexture> externalTexture = 503 entry.mResource.GetAsGPUExternalTexture(); 504 e.external_texture = externalTexture->GetId(); 505 externalTextures.AppendElement(externalTexture); 506 } else { 507 // Not a buffer, nor a texture view, nor a sampler, nor an external 508 // texture. If we pass this to wgpu_client, it'll panic. Log a warning 509 // instead and ignore this entry. 510 NS_WARNING("Bind group entry has unknown type."); 511 continue; 512 } 513 entries.AppendElement(e); 514 } 515 516 ffi::WGPUBindGroupDescriptor desc = {}; 517 518 webgpu::StringHelper label(aDesc.mLabel); 519 desc.label = label.Get(); 520 desc.layout = aDesc.mLayout->GetId(); 521 desc.entries = {entries.Elements(), entries.Length()}; 522 523 RawId id = ffi::wgpu_client_create_bind_group(GetClient(), GetId(), &desc); 524 525 RefPtr<BindGroup> object = new BindGroup(this, id, std::move(canvasContexts), 526 std::move(externalTextures)); 527 object->SetLabel(aDesc.mLabel); 528 529 return object.forget(); 530 } 531 532 void reportCompilationMessagesToConsole( 533 const RefPtr<ShaderModule>& aShaderModule, 534 const nsTArray<WebGPUCompilationMessage>& aMessages) { 535 auto* global = aShaderModule->GetParentObject(); 536 537 dom::AutoJSAPI api; 538 if (!api.Init(global)) { 539 return; 540 } 541 542 const auto& cx = api.cx(); 543 dom::GlobalObject globalObj(cx, global->GetGlobalJSObject()); 544 545 dom::Sequence<JS::Value> args; 546 dom::SequenceRooter<JS::Value> msgArgsRooter(cx, &args); 547 auto SetSingleStrAsArgs = 548 [&](const nsString& message, dom::Sequence<JS::Value>* args) 549 MOZ_CAN_RUN_SCRIPT { 550 args->Clear(); 551 JS::Rooted<JSString*> jsStr( 552 cx, JS_NewUCStringCopyN(cx, message.Data(), message.Length())); 553 if (!jsStr) { 554 return; 555 } 556 JS::Rooted<JS::Value> val(cx, JS::StringValue(jsStr)); 557 if (!args->AppendElement(val, fallible)) { 558 return; 559 } 560 }; 561 562 nsString label; 563 aShaderModule->GetLabel(label); 564 auto appendNiceLabelIfPresent = [&label](nsString* buf) MOZ_CAN_RUN_SCRIPT { 565 if (!label.IsEmpty()) { 566 buf->AppendLiteral(u" \""); 567 buf->Append(label); 568 buf->AppendLiteral(u"\""); 569 } 570 }; 571 572 // We haven't actually inspected a message for severity, but 573 // it doesn't actually matter, since we don't do anything at 574 // this level. 575 auto highestSeveritySeen = WebGPUCompilationMessageType::Info; 576 uint64_t errorCount = 0; 577 uint64_t warningCount = 0; 578 uint64_t infoCount = 0; 579 for (const auto& message : aMessages) { 580 bool higherThanSeen = 581 static_cast<std::underlying_type_t<WebGPUCompilationMessageType>>( 582 message.messageType) < 583 static_cast<std::underlying_type_t<WebGPUCompilationMessageType>>( 584 highestSeveritySeen); 585 if (higherThanSeen) { 586 highestSeveritySeen = message.messageType; 587 } 588 switch (message.messageType) { 589 case WebGPUCompilationMessageType::Error: 590 errorCount += 1; 591 break; 592 case WebGPUCompilationMessageType::Warning: 593 warningCount += 1; 594 break; 595 case WebGPUCompilationMessageType::Info: 596 infoCount += 1; 597 break; 598 } 599 } 600 switch (highestSeveritySeen) { 601 case WebGPUCompilationMessageType::Info: 602 // shouldn't happen, but :shrug: 603 break; 604 case WebGPUCompilationMessageType::Warning: { 605 nsString msg( 606 u"Encountered one or more warnings while creating shader module"); 607 appendNiceLabelIfPresent(&msg); 608 SetSingleStrAsArgs(msg, &args); 609 dom::Console::Warn(globalObj, args); 610 break; 611 } 612 case WebGPUCompilationMessageType::Error: { 613 nsString msg( 614 u"Encountered one or more errors while creating shader module"); 615 appendNiceLabelIfPresent(&msg); 616 SetSingleStrAsArgs(msg, &args); 617 dom::Console::Error(globalObj, args); 618 break; 619 } 620 } 621 622 nsString header; 623 header.AppendLiteral(u"WebGPU compilation info for shader module"); 624 appendNiceLabelIfPresent(&header); 625 header.AppendLiteral(u" ("); 626 header.AppendInt(errorCount); 627 header.AppendLiteral(u" error(s), "); 628 header.AppendInt(warningCount); 629 header.AppendLiteral(u" warning(s), "); 630 header.AppendInt(infoCount); 631 header.AppendLiteral(u" info)"); 632 SetSingleStrAsArgs(header, &args); 633 dom::Console::GroupCollapsed(globalObj, args); 634 635 for (const auto& message : aMessages) { 636 SetSingleStrAsArgs(message.message, &args); 637 switch (message.messageType) { 638 case WebGPUCompilationMessageType::Error: 639 dom::Console::Error(globalObj, args); 640 break; 641 case WebGPUCompilationMessageType::Warning: 642 dom::Console::Warn(globalObj, args); 643 break; 644 case WebGPUCompilationMessageType::Info: 645 dom::Console::Info(globalObj, args); 646 break; 647 } 648 } 649 dom::Console::GroupEnd(globalObj); 650 } 651 652 already_AddRefed<ShaderModule> Device::CreateShaderModule( 653 const dom::GPUShaderModuleDescriptor& aDesc, ErrorResult& aRv) { 654 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv); 655 if (NS_WARN_IF(aRv.Failed())) { 656 return nullptr; 657 } 658 659 webgpu::StringHelper label(aDesc.mLabel); 660 661 RawId moduleId = ffi::wgpu_client_create_shader_module( 662 GetClient(), GetId(), label.Get(), &aDesc.mCode); 663 664 RefPtr<ShaderModule> shaderModule = new ShaderModule(this, moduleId, promise); 665 666 shaderModule->SetLabel(aDesc.mLabel); 667 668 auto pending_promise = WebGPUChild::PendingCreateShaderModulePromise{ 669 RefPtr(promise), RefPtr(this), RefPtr(shaderModule)}; 670 GetChild()->mPendingCreateShaderModulePromises.push_back( 671 std::move(pending_promise)); 672 673 return shaderModule.forget(); 674 } 675 676 RawId CreateComputePipelineImpl(RawId deviceId, WebGPUChild* aChild, 677 const dom::GPUComputePipelineDescriptor& aDesc, 678 bool isAsync) { 679 ffi::WGPUComputePipelineDescriptor desc = {}; 680 nsCString entryPoint; 681 nsTArray<nsCString> constantKeys; 682 nsTArray<ffi::WGPUConstantEntry> constants; 683 684 webgpu::StringHelper label(aDesc.mLabel); 685 desc.label = label.Get(); 686 687 if (aDesc.mLayout.IsGPUAutoLayoutMode()) { 688 desc.layout = 0; 689 } else if (aDesc.mLayout.IsGPUPipelineLayout()) { 690 desc.layout = aDesc.mLayout.GetAsGPUPipelineLayout()->GetId(); 691 } else { 692 MOZ_ASSERT_UNREACHABLE(); 693 } 694 desc.stage.module = aDesc.mCompute.mModule->GetId(); 695 if (aDesc.mCompute.mEntryPoint.WasPassed()) { 696 CopyUTF16toUTF8(aDesc.mCompute.mEntryPoint.Value(), entryPoint); 697 desc.stage.entry_point = entryPoint.get(); 698 } else { 699 desc.stage.entry_point = nullptr; 700 } 701 if (aDesc.mCompute.mConstants.WasPassed()) { 702 const auto& descConstants = aDesc.mCompute.mConstants.Value().Entries(); 703 constantKeys.SetCapacity(descConstants.Length()); 704 constants.SetCapacity(descConstants.Length()); 705 for (const auto& entry : descConstants) { 706 ffi::WGPUConstantEntry constantEntry = {}; 707 nsCString key = NS_ConvertUTF16toUTF8(entry.mKey); 708 constantKeys.AppendElement(key); 709 constantEntry.key = key.get(); 710 constantEntry.value = entry.mValue; 711 constants.AppendElement(constantEntry); 712 } 713 desc.stage.constants = {constants.Elements(), constants.Length()}; 714 } 715 716 RawId id = ffi::wgpu_client_create_compute_pipeline(aChild->GetClient(), 717 deviceId, &desc, isAsync); 718 719 return id; 720 } 721 722 RawId CreateRenderPipelineImpl(RawId deviceId, WebGPUChild* aChild, 723 const dom::GPURenderPipelineDescriptor& aDesc, 724 bool isAsync) { 725 // A bunch of stack locals that we can have pointers into 726 nsTArray<ffi::WGPUVertexBufferLayout> vertexBuffers; 727 nsTArray<ffi::WGPUVertexAttribute> vertexAttributes; 728 ffi::WGPURenderPipelineDescriptor desc = {}; 729 nsCString vsEntry, fsEntry; 730 nsTArray<nsCString> vsConstantKeys, fsConstantKeys; 731 nsTArray<ffi::WGPUConstantEntry> vsConstants, fsConstants; 732 ffi::WGPUIndexFormat stripIndexFormat = ffi::WGPUIndexFormat_Uint16; 733 ffi::WGPUFace cullFace = ffi::WGPUFace_Front; 734 ffi::WGPUVertexState vertexState = {}; 735 ffi::WGPUFragmentState fragmentState = {}; 736 nsTArray<ffi::WGPUColorTargetState> colorStates; 737 nsTArray<ffi::WGPUBlendState> blendStates; 738 739 webgpu::StringHelper label(aDesc.mLabel); 740 desc.label = label.Get(); 741 742 if (aDesc.mLayout.IsGPUAutoLayoutMode()) { 743 desc.layout = 0; 744 } else if (aDesc.mLayout.IsGPUPipelineLayout()) { 745 desc.layout = aDesc.mLayout.GetAsGPUPipelineLayout()->GetId(); 746 } else { 747 MOZ_ASSERT_UNREACHABLE(); 748 } 749 750 { 751 const auto& stage = aDesc.mVertex; 752 vertexState.stage.module = stage.mModule->GetId(); 753 if (stage.mEntryPoint.WasPassed()) { 754 CopyUTF16toUTF8(stage.mEntryPoint.Value(), vsEntry); 755 vertexState.stage.entry_point = vsEntry.get(); 756 } else { 757 vertexState.stage.entry_point = nullptr; 758 } 759 if (stage.mConstants.WasPassed()) { 760 const auto& descConstants = stage.mConstants.Value().Entries(); 761 vsConstantKeys.SetCapacity(descConstants.Length()); 762 vsConstants.SetCapacity(descConstants.Length()); 763 for (const auto& entry : descConstants) { 764 ffi::WGPUConstantEntry constantEntry = {}; 765 nsCString key = NS_ConvertUTF16toUTF8(entry.mKey); 766 vsConstantKeys.AppendElement(key); 767 constantEntry.key = key.get(); 768 constantEntry.value = entry.mValue; 769 vsConstants.AppendElement(constantEntry); 770 } 771 vertexState.stage.constants = {vsConstants.Elements(), 772 vsConstants.Length()}; 773 } 774 775 for (const auto& vertex_desc : stage.mBuffers) { 776 ffi::WGPUVertexBufferLayout vb_desc = {}; 777 if (!vertex_desc.IsNull()) { 778 const auto& vd = vertex_desc.Value(); 779 vb_desc.array_stride = vd.mArrayStride; 780 vb_desc.step_mode = ffi::WGPUVertexStepMode(vd.mStepMode); 781 // Note: we are setting the length but not the pointer 782 vb_desc.attributes = {nullptr, vd.mAttributes.Length()}; 783 for (const auto& vat : vd.mAttributes) { 784 ffi::WGPUVertexAttribute ad = {}; 785 ad.offset = vat.mOffset; 786 ad.format = ConvertVertexFormat(vat.mFormat); 787 ad.shader_location = vat.mShaderLocation; 788 vertexAttributes.AppendElement(ad); 789 } 790 } 791 vertexBuffers.AppendElement(vb_desc); 792 } 793 // Now patch up all the pointers to attribute lists. 794 size_t numAttributes = 0; 795 for (auto& vb_desc : vertexBuffers) { 796 vb_desc.attributes.data = vertexAttributes.Elements() + numAttributes; 797 numAttributes += vb_desc.attributes.length; 798 } 799 800 vertexState.buffers = {vertexBuffers.Elements(), vertexBuffers.Length()}; 801 desc.vertex = &vertexState; 802 } 803 804 if (aDesc.mFragment.WasPassed()) { 805 const auto& stage = aDesc.mFragment.Value(); 806 fragmentState.stage.module = stage.mModule->GetId(); 807 if (stage.mEntryPoint.WasPassed()) { 808 CopyUTF16toUTF8(stage.mEntryPoint.Value(), fsEntry); 809 fragmentState.stage.entry_point = fsEntry.get(); 810 } else { 811 fragmentState.stage.entry_point = nullptr; 812 } 813 if (stage.mConstants.WasPassed()) { 814 const auto& descConstants = stage.mConstants.Value().Entries(); 815 fsConstantKeys.SetCapacity(descConstants.Length()); 816 fsConstants.SetCapacity(descConstants.Length()); 817 for (const auto& entry : descConstants) { 818 ffi::WGPUConstantEntry constantEntry = {}; 819 nsCString key = NS_ConvertUTF16toUTF8(entry.mKey); 820 fsConstantKeys.AppendElement(key); 821 constantEntry.key = key.get(); 822 constantEntry.value = entry.mValue; 823 fsConstants.AppendElement(constantEntry); 824 } 825 fragmentState.stage.constants = {fsConstants.Elements(), 826 fsConstants.Length()}; 827 } 828 829 // Note: we pre-collect the blend states into a different array 830 // so that we can have non-stale pointers into it. 831 for (const auto& colorState : stage.mTargets) { 832 ffi::WGPUColorTargetState desc = {}; 833 desc.format = ConvertTextureFormat(colorState.mFormat); 834 desc.write_mask = colorState.mWriteMask; 835 colorStates.AppendElement(desc); 836 ffi::WGPUBlendState bs = {}; 837 if (colorState.mBlend.WasPassed()) { 838 const auto& blend = colorState.mBlend.Value(); 839 bs.alpha = ConvertBlendComponent(blend.mAlpha); 840 bs.color = ConvertBlendComponent(blend.mColor); 841 } 842 blendStates.AppendElement(bs); 843 } 844 for (size_t i = 0; i < colorStates.Length(); ++i) { 845 if (stage.mTargets[i].mBlend.WasPassed()) { 846 colorStates[i].blend = &blendStates[i]; 847 } 848 } 849 850 fragmentState.targets = {colorStates.Elements(), colorStates.Length()}; 851 desc.fragment = &fragmentState; 852 } 853 854 { 855 const auto& prim = aDesc.mPrimitive; 856 desc.primitive.topology = ffi::WGPUPrimitiveTopology(prim.mTopology); 857 if (prim.mStripIndexFormat.WasPassed()) { 858 stripIndexFormat = ffi::WGPUIndexFormat(prim.mStripIndexFormat.Value()); 859 desc.primitive.strip_index_format = &stripIndexFormat; 860 } 861 desc.primitive.front_face = ffi::WGPUFrontFace(prim.mFrontFace); 862 if (prim.mCullMode != dom::GPUCullMode::None) { 863 cullFace = prim.mCullMode == dom::GPUCullMode::Front ? ffi::WGPUFace_Front 864 : ffi::WGPUFace_Back; 865 desc.primitive.cull_mode = &cullFace; 866 } 867 desc.primitive.unclipped_depth = prim.mUnclippedDepth; 868 } 869 desc.multisample = ConvertMultisampleState(aDesc.mMultisample); 870 871 ffi::WGPUDepthStencilState depthStencilState = {}; 872 if (aDesc.mDepthStencil.WasPassed()) { 873 depthStencilState = ConvertDepthStencilState(aDesc.mDepthStencil.Value()); 874 desc.depth_stencil = &depthStencilState; 875 } 876 877 RawId id = ffi::wgpu_client_create_render_pipeline(aChild->GetClient(), 878 deviceId, &desc, isAsync); 879 880 return id; 881 } 882 883 already_AddRefed<ComputePipeline> Device::CreateComputePipeline( 884 const dom::GPUComputePipelineDescriptor& aDesc) { 885 RawId pipelineId = 886 CreateComputePipelineImpl(GetId(), GetChild(), aDesc, false); 887 888 RefPtr<ComputePipeline> object = new ComputePipeline(this, pipelineId); 889 object->SetLabel(aDesc.mLabel); 890 return object.forget(); 891 } 892 893 already_AddRefed<RenderPipeline> Device::CreateRenderPipeline( 894 const dom::GPURenderPipelineDescriptor& aDesc) { 895 RawId pipelineId = 896 CreateRenderPipelineImpl(GetId(), GetChild(), aDesc, false); 897 898 RefPtr<RenderPipeline> object = new RenderPipeline(this, pipelineId); 899 object->SetLabel(aDesc.mLabel); 900 901 return object.forget(); 902 } 903 904 already_AddRefed<dom::Promise> Device::CreateComputePipelineAsync( 905 const dom::GPUComputePipelineDescriptor& aDesc, ErrorResult& aRv) { 906 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv); 907 if (NS_WARN_IF(aRv.Failed())) { 908 return nullptr; 909 } 910 911 RawId pipelineId = 912 CreateComputePipelineImpl(GetId(), GetChild(), aDesc, true); 913 914 auto pending_promise = WebGPUChild::PendingCreatePipelinePromise{ 915 RefPtr(promise), RefPtr(this), false, pipelineId, aDesc.mLabel}; 916 GetChild()->mPendingCreatePipelinePromises.push_back( 917 std::move(pending_promise)); 918 919 return promise.forget(); 920 } 921 922 already_AddRefed<dom::Promise> Device::CreateRenderPipelineAsync( 923 const dom::GPURenderPipelineDescriptor& aDesc, ErrorResult& aRv) { 924 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv); 925 if (NS_WARN_IF(aRv.Failed())) { 926 return nullptr; 927 } 928 929 RawId pipelineId = CreateRenderPipelineImpl(GetId(), GetChild(), aDesc, true); 930 931 auto pending_promise = WebGPUChild::PendingCreatePipelinePromise{ 932 RefPtr(promise), RefPtr(this), true, pipelineId, aDesc.mLabel}; 933 GetChild()->mPendingCreatePipelinePromises.push_back( 934 std::move(pending_promise)); 935 936 return promise.forget(); 937 } 938 939 already_AddRefed<Texture> Device::InitSwapChain( 940 const dom::GPUCanvasConfiguration* const aConfig, 941 const layers::RemoteTextureOwnerId aOwnerId, 942 bool aUseSharedTextureInSwapChain, gfx::SurfaceFormat aFormat, 943 gfx::IntSize aCanvasSize) { 944 MOZ_ASSERT(aConfig); 945 946 // Check that aCanvasSize and aFormat will generate a texture stride 947 // within limits. 948 const auto bufferStrideWithMask = BufferStrideWithMask(aCanvasSize, aFormat); 949 if (!bufferStrideWithMask.isValid()) { 950 return nullptr; 951 } 952 953 const layers::RGBDescriptor rgbDesc(aCanvasSize, aFormat); 954 955 ffi::wgpu_client_create_swap_chain( 956 GetClient(), GetId(), mQueue->GetId(), rgbDesc.size().Width(), 957 rgbDesc.size().Height(), (int8_t)rgbDesc.format(), aOwnerId.mId, 958 aUseSharedTextureInSwapChain); 959 960 // TODO: `mColorSpace`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846608> 961 // TODO: `mAlphaMode`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846605> 962 return CreateTextureForSwapChain(aConfig, aCanvasSize, aOwnerId); 963 } 964 965 bool Device::CheckNewWarning(const nsACString& aMessage) { 966 return mKnownWarnings.EnsureInserted(aMessage); 967 } 968 969 void Device::Destroy() { 970 // Unmap all buffers from this device, as specified by 971 // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy. 972 dom::AutoJSAPI jsapi; 973 if (jsapi.Init(GetOwnerGlobal())) { 974 IgnoredErrorResult rv; 975 for (const auto& buffer : mTrackedBuffers) { 976 buffer->Unmap(jsapi.cx(), rv); 977 } 978 979 mTrackedBuffers.Clear(); 980 } 981 982 ffi::wgpu_client_destroy_device(GetClient(), GetId()); 983 984 if (mLostPromise->State() != dom::Promise::PromiseState::Pending) { 985 return; 986 } 987 RefPtr<dom::Promise> pending_promise = mLostPromise; 988 GetChild()->mPendingDeviceLostPromises.insert( 989 {GetId(), std::move(pending_promise)}); 990 } 991 992 void Device::PushErrorScope(const dom::GPUErrorFilter& aFilter) { 993 ffi::wgpu_client_push_error_scope(GetClient(), GetId(), (uint8_t)aFilter); 994 } 995 996 already_AddRefed<dom::Promise> Device::PopErrorScope(ErrorResult& aRv) { 997 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv); 998 if (NS_WARN_IF(aRv.Failed())) { 999 return nullptr; 1000 } 1001 1002 ffi::wgpu_client_pop_error_scope(GetClient(), GetId()); 1003 1004 auto pending_promise = 1005 WebGPUChild::PendingPopErrorScopePromise{RefPtr(promise), RefPtr(this)}; 1006 GetChild()->mPendingPopErrorScopePromises.push_back( 1007 std::move(pending_promise)); 1008 1009 return promise.forget(); 1010 } 1011 1012 } // namespace mozilla::webgpu