ExternalTexture.cpp (50731B)
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 "ExternalTexture.h" 7 8 #include "Colorspaces.h" 9 #include "ImageContainer.h" 10 #include "mozilla/Assertions.h" 11 #include "mozilla/ErrorResult.h" 12 #include "mozilla/dom/HTMLVideoElement.h" 13 #include "mozilla/dom/VideoFrame.h" 14 #include "mozilla/dom/WebGPUBinding.h" 15 #include "mozilla/gfx/Logging.h" 16 #include "mozilla/gfx/Types.h" 17 #include "mozilla/layers/ImageDataSerializer.h" 18 #include "mozilla/layers/LayersSurfaces.h" 19 #include "mozilla/layers/TextureHost.h" 20 #include "mozilla/layers/VideoBridgeParent.h" 21 #include "mozilla/webgpu/Queue.h" 22 #include "mozilla/webgpu/Utility.h" 23 #include "mozilla/webgpu/WebGPUChild.h" 24 #include "mozilla/webgpu/WebGPUParent.h" 25 #include "nsLayoutUtils.h" 26 #include "nsPrintfCString.h" 27 28 #ifdef XP_WIN 29 # include "mozilla/layers/CompositeProcessD3D11FencesHolderMap.h" 30 # include "mozilla/layers/GpuProcessD3D11TextureMap.h" 31 # include "mozilla/layers/TextureD3D11.h" 32 #endif 33 #ifdef XP_MACOSX 34 # include "mozilla/gfx/MacIOSurface.h" 35 # include "mozilla/layers/MacIOSurfaceTextureHostOGL.h" 36 #endif 37 38 namespace mozilla::webgpu { 39 40 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(ExternalTexture, mParent) 41 GPU_IMPL_JS_WRAP(ExternalTexture) 42 43 ExternalTexture::ExternalTexture(Device* const aParent, RawId aId, 44 RefPtr<ExternalTextureSourceClient> aSource) 45 : ObjectBase(aParent->GetChild(), aId, 46 ffi::wgpu_client_drop_external_texture), 47 ChildOf(aParent), 48 mSource(aSource) {} 49 50 ExternalTexture::~ExternalTexture() = default; 51 52 /* static */ already_AddRefed<ExternalTexture> ExternalTexture::Create( 53 Device* const aParent, const nsString& aLabel, 54 const RefPtr<ExternalTextureSourceClient>& aSource, 55 dom::PredefinedColorSpace aColorSpace) { 56 const webgpu::StringHelper label(aLabel); 57 const ffi::WGPUPredefinedColorSpace colorSpace = 58 ConvertPredefinedColorSpace(aColorSpace); 59 const ffi::WGPUExternalTextureDescriptor desc = { 60 .label = label.Get(), 61 .source = aSource ? aSource->GetId() : 0, 62 .color_space = colorSpace, 63 }; 64 65 const RawId id = ffi::wgpu_client_create_external_texture( 66 aParent->GetClient(), aParent->GetId(), &desc); 67 68 RefPtr<ExternalTexture> externalTexture = 69 new ExternalTexture(aParent, id, aSource); 70 externalTexture->SetLabel(aLabel); 71 72 return externalTexture.forget(); 73 } 74 75 void ExternalTexture::Expire() { 76 mIsExpired = true; 77 MaybeDestroy(); 78 } 79 80 void ExternalTexture::Unexpire() { 81 MOZ_ASSERT(!mIsDestroyed); 82 MOZ_ASSERT(mSource); 83 mIsExpired = false; 84 } 85 86 void ExternalTexture::OnSubmit(uint64_t aSubmissionIndex) { 87 mLastSubmittedIndex = aSubmissionIndex; 88 } 89 90 void ExternalTexture::OnSubmittedWorkDone(uint64_t aSubmissionIndex) { 91 mLastSubmittedWorkDoneIndex = aSubmissionIndex; 92 MaybeDestroy(); 93 } 94 95 void ExternalTexture::MaybeDestroy() { 96 if (!mIsDestroyed && mIsExpired && 97 mLastSubmittedWorkDoneIndex >= mLastSubmittedIndex) { 98 mIsDestroyed = true; 99 mSource = nullptr; 100 // We could be cleverer and keep the external texture alive until its 101 // source is destroyed and there's no chance we could want to reuse the 102 // external texture. But that would complicate the logic and perhaps not 103 // even gain all that much, as typically attempts to reuse the external 104 // texture will occur before the previously submitted work is done, so will 105 // be successful anyway. 106 ffi::wgpu_client_destroy_external_texture(GetClient(), GetId()); 107 } 108 } 109 110 RefPtr<ExternalTexture> ExternalTextureCache::GetOrCreate( 111 Device* aDevice, const dom::GPUExternalTextureDescriptor& aDesc, 112 ErrorResult& aRv) { 113 const RefPtr<ExternalTextureSourceClient> source = 114 GetOrCreateSource(aDevice, aDesc.mSource, aRv); 115 116 if (source) { 117 return source->GetOrCreateExternalTexture(aDevice, aDesc); 118 } 119 120 // Create external texture with a null source to indicate error state. 121 return ExternalTexture::Create(aDevice, aDesc.mLabel, nullptr, 122 aDesc.mColorSpace); 123 } 124 125 RefPtr<ExternalTextureSourceClient> ExternalTextureCache::GetOrCreateSource( 126 Device* aDevice, const dom::OwningHTMLVideoElementOrVideoFrame& aSource, 127 ErrorResult& aRv) { 128 RefPtr<layers::Image> image; 129 switch (aSource.GetType()) { 130 case dom::OwningHTMLVideoElementOrVideoFrame::Type::eHTMLVideoElement: 131 image = aSource.GetAsHTMLVideoElement()->GetCurrentImage(); 132 break; 133 case dom::OwningHTMLVideoElementOrVideoFrame::Type::eVideoFrame: 134 image = aSource.GetAsVideoFrame()->GetImage(); 135 break; 136 } 137 138 typename decltype(mSources)::AddPtr p; 139 if (image) { 140 p = mSources.lookupForAdd(image->GetSerial()); 141 if (p) { 142 const RefPtr<ExternalTextureSourceClient> source = p->value(); 143 MOZ_ASSERT(source->mImage == image); 144 return source; 145 } 146 } 147 148 // If we didn't find an image above we know this is going to fail, but call 149 // it anyway so that we can keep all our error handling in one place. 150 const RefPtr<ExternalTextureSourceClient> source = 151 ExternalTextureSourceClient::Create(aDevice, this, aSource, aRv); 152 if (source) { 153 // If creating the source succeeded, we must have found an image, which 154 // means we must have a valid AddPtr from above. 155 // An OOM error in add() just means we don't get to cache the source, but we 156 // can still proceed. 157 (void)mSources.add(p, source->mImage->GetSerial(), source); 158 } 159 return source; 160 } 161 162 void ExternalTextureCache::RemoveSource( 163 const ExternalTextureSourceClient* aSource) { 164 mSources.remove(aSource->mImage->GetSerial()); 165 } 166 167 ExternalTextureSourceClient::ExternalTextureSourceClient( 168 WebGPUChild* aChild, RawId aId, ExternalTextureCache* aCache, 169 const RefPtr<layers::Image>& aImage, 170 const std::array<RawId, 3>& aTextureIds, 171 const std::array<RawId, 3>& aViewIds) 172 : ObjectBase(aChild, aId, ffi::wgpu_client_drop_external_texture_source), 173 mImage(aImage), 174 mTextureIds(std::move(aTextureIds)), 175 mViewIds(std::move(aViewIds)), 176 mCache(aCache) { 177 MOZ_RELEASE_ASSERT(aId); 178 } 179 180 ExternalTextureSourceClient::~ExternalTextureSourceClient() { 181 if (mCache) { 182 mCache->RemoveSource(this); 183 } 184 185 // Call destroy() in addition to drop() to ensure the plane textures are 186 // destroyed immediately. Otherwise they will remain alive until any external 187 // textures/bind groups referencing them are garbage collected, which can 188 // quickly result in excessive memory usage. 189 ffi::wgpu_client_destroy_external_texture_source(GetClient(), GetId()); 190 // Usually we'd just drop() the textures and views, which would in turn free 191 // their IDs. However, we don't know which IDs were used by the host to 192 // actually create textures and views with. Therefore the host side is 193 // responsible for dropping the textures and views that it actually created, 194 // but the client side must free all of the IDs that were made. 195 for (const auto id : mViewIds) { 196 wgpu_client_free_texture_view_id(GetClient(), id); 197 } 198 for (const auto id : mTextureIds) { 199 wgpu_client_free_texture_id(GetClient(), id); 200 } 201 } 202 203 /* static */ already_AddRefed<ExternalTextureSourceClient> 204 ExternalTextureSourceClient::Create( 205 Device* aDevice, ExternalTextureCache* aCache, 206 const dom::OwningHTMLVideoElementOrVideoFrame& aSource, ErrorResult& aRv) { 207 // Obtain the layers::Image from the HTMLVideoElement or VideoFrame. We use 208 // nsLayoutUtils::SurfaceFrom*() instead of directly fetching the image, as it 209 // helps with the security checks below. It also helpfully determines the 210 // (coded) size, intrinsic size, and crop rect fields for us. Passing 211 // SFE_ALLOW_UNCROPPED_UNSCALED ensures it does not create a source surface, 212 // as we are able to handle the cropping and scaling ourself. 213 const uint32_t flags = nsLayoutUtils::SFE_ALLOW_UNCROPPED_UNSCALED; 214 SurfaceFromElementResult sfeResult; 215 VideoRotation rotation; 216 switch (aSource.GetType()) { 217 case dom::OwningHTMLVideoElementOrVideoFrame::Type::eHTMLVideoElement: { 218 const auto& videoElement = aSource.GetAsHTMLVideoElement(); 219 sfeResult = nsLayoutUtils::SurfaceFromElement(videoElement.get(), flags); 220 rotation = videoElement->RotationDegrees(); 221 } break; 222 case dom::OwningHTMLVideoElementOrVideoFrame::Type::eVideoFrame: { 223 const auto& videoFrame = aSource.GetAsVideoFrame(); 224 sfeResult = nsLayoutUtils::SurfaceFromVideoFrame(videoFrame.get(), flags); 225 rotation = VideoRotation::kDegree_0; 226 } break; 227 } 228 229 // If source is not origin-clean, throw a SecurityError and return. 230 // https://www.w3.org/TR/webgpu/#dom-gpudevice-importexternaltexture 231 if (!sfeResult.mCORSUsed) { 232 const nsIGlobalObject* const global = aDevice->GetOwnerGlobal(); 233 nsIPrincipal* const dstPrincipal = 234 global ? global->PrincipalOrNull() : nullptr; 235 if (!sfeResult.mPrincipal || !dstPrincipal || 236 !dstPrincipal->Subsumes(sfeResult.mPrincipal)) { 237 aRv.ThrowSecurityError("Cross-origin elements require CORS!"); 238 return nullptr; 239 } 240 } 241 if (sfeResult.mIsWriteOnly) { 242 aRv.ThrowSecurityError("Write only source data not supported!"); 243 return nullptr; 244 } 245 246 const auto child = aDevice->GetChild(); 247 248 // Let usability be ? check the usability of the image argument(source). 249 // If usability is not good: 250 // 1. Generate a validation error. 251 // 2. Return an invalidated GPUExternalTexture. 252 // https://www.w3.org/TR/webgpu/#dom-gpudevice-importexternaltexture 253 const RefPtr<layers::Image> image = sfeResult.mLayersImage; 254 if (!image) { 255 ffi::wgpu_report_validation_error(child->GetClient(), aDevice->GetId(), 256 "Video source's usability is bad"); 257 return nullptr; 258 } 259 260 layers::SurfaceDescriptor sd; 261 const nsresult rv = image->BuildSurfaceDescriptorGPUVideoOrBuffer( 262 sd, layers::Image::BuildSdbFlags::Default, Nothing(), 263 [&](uint32_t aBufferSize) { 264 ipc::Shmem buffer; 265 if (!child->AllocShmem(aBufferSize, &buffer)) { 266 return layers::MemoryOrShmem(); 267 } 268 return layers::MemoryOrShmem(std::move(buffer)); 269 }, 270 [&](layers::MemoryOrShmem&& aBuffer) { 271 child->DeallocShmem(aBuffer.get_Shmem()); 272 }); 273 if (NS_FAILED(rv)) { 274 gfxCriticalErrorOnce() << "BuildSurfaceDescriptorGPUVideoOrBuffer failed"; 275 ffi::wgpu_report_internal_error( 276 child->GetClient(), aDevice->GetId(), 277 "BuildSurfaceDescriptorGPUVideoOrBuffer failed"); 278 return nullptr; 279 } 280 281 const auto sourceId = 282 ffi::wgpu_client_make_external_texture_source_id(child->GetClient()); 283 // We don't know how many textures or views the host side will need, so make 284 // enough IDs for up to 3 of each. 285 const std::array<RawId, 3> textureIds{ 286 ffi::wgpu_client_make_texture_id(child->GetClient()), 287 ffi::wgpu_client_make_texture_id(child->GetClient()), 288 ffi::wgpu_client_make_texture_id(child->GetClient()), 289 }; 290 const std::array<RawId, 3> viewIds{ 291 ffi::wgpu_client_make_texture_view_id(child->GetClient()), 292 ffi::wgpu_client_make_texture_view_id(child->GetClient()), 293 ffi::wgpu_client_make_texture_view_id(child->GetClient()), 294 }; 295 296 // The actual size of the surface (possibly including non-visible padding). 297 // This has not been adjusted for any rotation. 298 const gfx::IntSize codedSize = sfeResult.mSize; 299 // The crop rectangle to be displayed, defaulting to the full surface if not 300 // provided. This is relative to the coded size, and again has not been 301 // adjusted for any rotation. 302 const gfx::IntRect cropRect = 303 sfeResult.mCropRect.valueOr(gfx::IntRect({}, codedSize)); 304 // The size the surface is intended to be rendered at. We use this for the 305 // external texture descriptor's size field which will be the size reported 306 // to web content, eg via WGSL's `textureDimensions()` builtin. This has had 307 // rotation taken into account. 308 const gfx::IntSize intrinsicSize = sfeResult.mIntrinsicSize; 309 310 // Calculate the sample transform, starting with the rotation. As only 90 311 // degree increments are supported, we hard-code the values to avoid 312 // expensive trig and to keep the numbers precise. If/when we support flips 313 // we'd handle that here too. 314 gfx::Matrix sampleTransform; 315 switch (rotation) { 316 case VideoRotation::kDegree_0: 317 break; 318 case VideoRotation::kDegree_90: 319 sampleTransform = gfx::Matrix(0.0, -1.0, 1.0, 0.0, 0.0, 1.0); 320 break; 321 case VideoRotation::kDegree_180: 322 sampleTransform = gfx::Matrix(-1.0, 0.0, 0.0, -1.0, 1.0, 1.0); 323 break; 324 case VideoRotation::kDegree_270: 325 sampleTransform = gfx::Matrix(0.0, 1.0, -1.0, 0.0, 1.0, 0.0); 326 break; 327 } 328 329 // Scale and translate to account for the crop rect. We need to ensure that 330 // the normalized coordinates (0,0)..(1,1) map to the crop rect rather than 331 // the coded size. We must therefore normalize the crop rect by dividing by 332 // the coded size, then scale and translate the transform based on the 333 // normalized crop rect. We apply these transformations pre-rotation as the 334 // crop rect itself is expressed pre-rotation. Note the intrinsic size is 335 // irrelevant here as we are dealing with normalized coordinates. 336 gfx::Rect normalizedCropRect = gfx::Rect(cropRect); 337 normalizedCropRect.Scale(1.0 / static_cast<float>(codedSize.width), 338 1.0 / static_cast<float>(codedSize.height)); 339 sampleTransform.PreTranslate(normalizedCropRect.x, normalizedCropRect.y); 340 sampleTransform.PreScale(normalizedCropRect.Width(), 341 normalizedCropRect.Height()); 342 343 // Derive the load transform from the sample transform. Texture loads accept 344 // unnormalized texel coordinates ranging from (0,0) to the intrinsic size 345 // minus one, i.e. based on the size the external texture reports itself as 346 // to web content. We need to map these to our rotated crop rect, and the end 347 // result must be texel coordinates based on the actual texture size. This 348 // can be achieved by first normalizing the coordinates by dividing by the 349 // intrinsic size minus one, then applying the sample transformation, then 350 // unnormalizing the transformed coordinates by multiplying by the actual 351 // texture size minus one. 352 gfx::Matrix loadTransform = sampleTransform; 353 loadTransform.PreScale( 354 1.0 / static_cast<float>(std::max(intrinsicSize.width - 1, 1)), 355 1.0 / static_cast<float>(std::max(intrinsicSize.height - 1, 1))); 356 loadTransform.PostScale(static_cast<float>(codedSize.width - 1), 357 static_cast<float>(codedSize.height - 1)); 358 359 const ExternalTextureSourceDescriptor sourceDesc = { 360 .mTextureIds = textureIds, 361 .mViewIds = viewIds, 362 .mSurfaceDescriptor = std::move(sd), 363 .mSize = intrinsicSize, 364 .mSampleTransform = {sampleTransform._11, sampleTransform._12, 365 sampleTransform._21, sampleTransform._22, 366 sampleTransform._31, sampleTransform._32}, 367 .mLoadTransform = {loadTransform._11, loadTransform._12, 368 loadTransform._21, loadTransform._22, 369 loadTransform._31, loadTransform._32}, 370 }; 371 372 // We use a separate IPDL message than Messages() so that IPDL can handle the 373 // SurfaceDescriptor (de)serialization for us. We must therefore flush any 374 // queued messages first so that they are processed in the correct order. 375 child->FlushQueuedMessages(); 376 child->SendCreateExternalTextureSource( 377 aDevice->GetId(), aDevice->GetQueue()->GetId(), sourceId, sourceDesc); 378 379 RefPtr<ExternalTextureSourceClient> source = new ExternalTextureSourceClient( 380 child, sourceId, aCache, image, textureIds, viewIds); 381 return source.forget(); 382 } 383 384 RefPtr<ExternalTexture> ExternalTextureSourceClient::GetOrCreateExternalTexture( 385 Device* aDevice, const dom::GPUExternalTextureDescriptor& aDesc) { 386 auto p = mExternalTextures.lookupForAdd(aDesc.mColorSpace); 387 if (p) { 388 if (auto* const externalTexture = p->value().get()) { 389 if (!externalTexture->IsDestroyed()) { 390 externalTexture->Unexpire(); 391 return externalTexture; 392 } 393 } 394 } 395 396 const RefPtr<ExternalTexture> externalTexture = 397 ExternalTexture::Create(aDevice, aDesc.mLabel, this, aDesc.mColorSpace); 398 399 if (externalTexture) { 400 if (p) { 401 p->value() = externalTexture; 402 } else { 403 // OOM error in add() just means we don't get to cache the external 404 // texture, but we can still proceed. 405 (void)mExternalTextures.add(p, aDesc.mColorSpace, externalTexture); 406 } 407 } 408 409 return externalTexture; 410 } 411 412 ExternalTextureSourceHost::ExternalTextureSourceHost( 413 Span<const RawId> aTextureIds, Span<const RawId> aViewIds, 414 gfx::IntSize aSize, gfx::SurfaceFormat aFormat, 415 gfx::YUVRangedColorSpace aColorSpace, 416 const std::array<float, 6>& aSampleTransform, 417 const std::array<float, 6>& aLoadTransform) 418 : mSize(aSize), 419 mFormat(aFormat), 420 mColorSpace(aColorSpace), 421 mSampleTransform(aSampleTransform), 422 mLoadTransform(aLoadTransform) { 423 mTextureIds.AppendElements(aTextureIds); 424 mViewIds.AppendElements(aViewIds); 425 } 426 427 /* static */ ExternalTextureSourceHost ExternalTextureSourceHost::Create( 428 WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId, 429 const ExternalTextureSourceDescriptor& aDesc) { 430 const auto& sd = aDesc.mSurfaceDescriptor; 431 switch (sd.type()) { 432 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: { 433 const layers::SurfaceDescriptorBuffer& bufferDesc = 434 sd.get_SurfaceDescriptorBuffer(); 435 ipc::Shmem& bufferShmem = bufferDesc.data().get_Shmem(); 436 auto source = 437 CreateFromBufferDesc(aParent, aDeviceId, aQueueId, aDesc, 438 bufferDesc.desc(), bufferShmem.Range<uint8_t>()); 439 aParent->DeallocShmem(bufferShmem); 440 return source; 441 } break; 442 443 case layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo: { 444 const layers::SurfaceDescriptorGPUVideo& gpuVideoDesc = 445 sd.get_SurfaceDescriptorGPUVideo(); 446 const layers::SurfaceDescriptorRemoteDecoder& remoteDecoderDesc = 447 gpuVideoDesc.get_SurfaceDescriptorRemoteDecoder(); 448 449 const auto videoBridge = 450 layers::VideoBridgeParent::GetSingleton(remoteDecoderDesc.source()); 451 if (!videoBridge) { 452 gfxCriticalErrorOnce() << "Failed to get VideoBridge"; 453 aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal, 454 "Failed to get VideoBridge"_ns); 455 return CreateError(); 456 } 457 const RefPtr<layers::TextureHost> textureHost = 458 videoBridge->LookupTexture(aParent->mContentId, 459 remoteDecoderDesc.handle()); 460 if (!textureHost) { 461 gfxCriticalErrorOnce() << "Failed to lookup remote decoder texture"; 462 aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal, 463 "Failed to lookup remote decoder texture"_ns); 464 return CreateError(); 465 } 466 467 if (const auto* bufferHost = textureHost->AsBufferTextureHost()) { 468 return CreateFromBufferDesc( 469 aParent, aDeviceId, aQueueId, aDesc, 470 bufferHost->GetBufferDescriptor(), 471 Span(bufferHost->GetBuffer(), bufferHost->GetBufferSize())); 472 } else if (const auto* dxgiHost = textureHost->AsDXGITextureHostD3D11()) { 473 return CreateFromDXGITextureHost(aParent, aDeviceId, aQueueId, aDesc, 474 dxgiHost); 475 } else if (const auto* dxgiYCbCrHost = 476 textureHost->AsDXGIYCbCrTextureHostD3D11()) { 477 return CreateFromDXGIYCbCrTextureHost(aParent, aDeviceId, aQueueId, 478 aDesc, dxgiYCbCrHost); 479 } else if (const auto* ioSurfHost = 480 textureHost->AsMacIOSurfaceTextureHost()) { 481 return CreateFromMacIOSurfaceTextureHost(aParent, aDeviceId, aDesc, 482 ioSurfHost); 483 } else { 484 gfxCriticalErrorOnce() 485 << "Unexpected SurfaceDescriptorGPUVideo TextureHost type"; 486 aParent->ReportError( 487 aDeviceId, dom::GPUErrorFilter::Internal, 488 "Unexpected SurfaceDescriptorGPUVideo TextureHost type"_ns); 489 return CreateError(); 490 } 491 } break; 492 default: 493 gfxCriticalErrorOnce() 494 << "Unexpected SurfaceDescriptor type: " << sd.type(); 495 aParent->ReportError( 496 aDeviceId, dom::GPUErrorFilter::Internal, 497 nsPrintfCString("Unexpected SurfaceDescriptor type: %d", sd.type())); 498 return CreateError(); 499 } 500 return CreateError(); 501 } 502 503 /* static */ ExternalTextureSourceHost 504 ExternalTextureSourceHost::CreateFromBufferDesc( 505 WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId, 506 const ExternalTextureSourceDescriptor& aDesc, 507 const layers::BufferDescriptor& aBufferDesc, Span<uint8_t> aBuffer) { 508 const gfx::SurfaceFormat format = 509 layers::ImageDataSerializer::FormatFromBufferDescriptor(aBufferDesc); 510 // Creates a texture and view for a single plane, and writes the provided data 511 // to the texture. 512 auto createPlane = [aParent, aDeviceId, aQueueId]( 513 RawId texId, RawId viewId, 514 ffi::WGPUTextureFormat format, gfx::IntSize size, 515 Span<uint8_t> buffer, uint32_t stride) { 516 const ffi::WGPUTextureDescriptor textureDesc{ 517 .size = 518 ffi::WGPUExtent3d{ 519 .width = static_cast<uint32_t>(size.width), 520 .height = static_cast<uint32_t>(size.height), 521 .depth_or_array_layers = 1, 522 }, 523 .mip_level_count = 1, 524 .sample_count = 1, 525 .dimension = ffi::WGPUTextureDimension_D2, 526 .format = format, 527 .usage = WGPUTextureUsages_TEXTURE_BINDING | WGPUTextureUsages_COPY_DST, 528 .view_formats = {}, 529 }; 530 531 { 532 ErrorBuffer error; 533 ffi::wgpu_server_device_create_texture( 534 aParent->GetContext(), aDeviceId, texId, &textureDesc, error.ToFFI()); 535 // Since we have full control over the creation of this texture, any 536 // validation error we encounter should be treated as an internal error. 537 error.CoerceValidationToInternal(); 538 aParent->ForwardError(error); 539 } 540 541 const ffi::WGPUTexelCopyTextureInfo dest{ 542 .texture = texId, 543 .mip_level = 0, 544 .origin = {}, 545 .aspect = ffi::WGPUTextureAspect_All, 546 }; 547 548 const ffi::WGPUTexelCopyBufferLayout layout{ 549 .offset = 0, 550 .bytes_per_row = &stride, 551 .rows_per_image = nullptr, 552 }; 553 const Span<uint8_t> slice = buffer.to(size.height * stride); 554 const ffi::WGPUFfiSlice_u8 data{ 555 .data = slice.data(), 556 .length = slice.size(), 557 }; 558 { 559 ErrorBuffer error; 560 ffi::wgpu_server_queue_write_texture(aParent->GetContext(), aDeviceId, 561 aQueueId, &dest, data, &layout, 562 &textureDesc.size, error.ToFFI()); 563 error.CoerceValidationToInternal(); 564 aParent->ForwardError(error); 565 } 566 567 const ffi::WGPUTextureViewDescriptor viewDesc{}; 568 { 569 ErrorBuffer error; 570 ffi::wgpu_server_texture_create_view(aParent->GetContext(), aDeviceId, 571 texId, viewId, &viewDesc, 572 error.ToFFI()); 573 error.CoerceValidationToInternal(); 574 aParent->ForwardError(error); 575 } 576 }; 577 578 AutoTArray<RawId, 3> usedTextureIds; 579 AutoTArray<RawId, 3> usedViewIds; 580 gfx::YUVRangedColorSpace colorSpace; 581 switch (aBufferDesc.type()) { 582 case layers::BufferDescriptor::TRGBDescriptor: { 583 const layers::RGBDescriptor& rgbDesc = aBufferDesc.get_RGBDescriptor(); 584 ffi::WGPUTextureFormat planeFormat; 585 switch (rgbDesc.format()) { 586 case gfx::SurfaceFormat::B8G8R8A8: 587 case gfx::SurfaceFormat::B8G8R8X8: 588 planeFormat = {ffi::WGPUTextureFormat_Bgra8Unorm}; 589 break; 590 case gfx::SurfaceFormat::R8G8B8A8: 591 case gfx::SurfaceFormat::R8G8B8X8: 592 planeFormat = {ffi::WGPUTextureFormat_Rgba8Unorm}; 593 break; 594 default: 595 gfxCriticalErrorOnce() 596 << "Unexpected RGBDescriptor format: " << rgbDesc.format(); 597 aParent->ReportError( 598 aDeviceId, dom::GPUErrorFilter::Internal, 599 nsPrintfCString("Unexpected RGBDescriptor format: %s", 600 mozilla::ToString(rgbDesc.format()).c_str())); 601 return CreateError(); 602 } 603 createPlane(aDesc.mTextureIds[0], aDesc.mViewIds[0], planeFormat, 604 rgbDesc.size(), aBuffer, 605 layers::ImageDataSerializer::GetRGBStride(rgbDesc)); 606 usedTextureIds.AppendElement(aDesc.mTextureIds[0]); 607 usedViewIds.AppendElement(aDesc.mViewIds[0]); 608 colorSpace = gfx::YUVRangedColorSpace::GbrIdentity; 609 } break; 610 case layers::BufferDescriptor::TYCbCrDescriptor: { 611 const layers::YCbCrDescriptor& yCbCrDesc = 612 aBufferDesc.get_YCbCrDescriptor(); 613 const gfx::IntSize ySize = 614 layers::ImageDataSerializer::SizeFromBufferDescriptor(aBufferDesc); 615 const gfx::IntSize cbCrSize = 616 layers::ImageDataSerializer::GetCroppedCbCrSize(aBufferDesc); 617 618 ffi::WGPUTextureFormat planeFormat; 619 switch (yCbCrDesc.colorDepth()) { 620 case gfx::ColorDepth::COLOR_8: 621 planeFormat = {ffi::WGPUTextureFormat_R8Unorm}; 622 break; 623 case gfx::ColorDepth::COLOR_10: 624 case gfx::ColorDepth::COLOR_12: 625 case gfx::ColorDepth::COLOR_16: 626 gfxCriticalNoteOnce << "Unsupported color depth: " 627 << yCbCrDesc.colorDepth(); 628 aParent->ReportError( 629 aDeviceId, dom::GPUErrorFilter::Internal, 630 nsPrintfCString( 631 "Unsupported color depth: %s", 632 mozilla::ToString(yCbCrDesc.colorDepth()).c_str())); 633 return CreateError(); 634 } 635 636 createPlane(aDesc.mTextureIds[0], aDesc.mViewIds[0], planeFormat, ySize, 637 aBuffer.from(yCbCrDesc.yOffset()), yCbCrDesc.yStride()); 638 createPlane(aDesc.mTextureIds[1], aDesc.mViewIds[1], planeFormat, 639 cbCrSize, aBuffer.from(yCbCrDesc.cbOffset()), 640 yCbCrDesc.cbCrStride()); 641 createPlane(aDesc.mTextureIds[2], aDesc.mViewIds[2], planeFormat, 642 cbCrSize, aBuffer.from(yCbCrDesc.crOffset()), 643 yCbCrDesc.cbCrStride()); 644 usedTextureIds.AppendElements(aDesc.mTextureIds.data(), 645 aDesc.mTextureIds.size()); 646 usedViewIds.AppendElements(aDesc.mViewIds.data(), aDesc.mViewIds.size()); 647 colorSpace = gfx::ToYUVRangedColorSpace(yCbCrDesc.yUVColorSpace(), 648 yCbCrDesc.colorRange()); 649 } break; 650 case layers::BufferDescriptor::T__None: { 651 gfxCriticalErrorOnce() << "Invalid BufferDescriptor"; 652 aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal, 653 "Invalid BufferDescriptor"_ns); 654 return CreateError(); 655 } break; 656 } 657 658 return ExternalTextureSourceHost(usedTextureIds, usedViewIds, aDesc.mSize, 659 format, colorSpace, aDesc.mSampleTransform, 660 aDesc.mLoadTransform); 661 } 662 663 /* static */ ExternalTextureSourceHost 664 ExternalTextureSourceHost::CreateError() { 665 return ExternalTextureSourceHost( 666 {}, {}, gfx::IntSize(0, 0), gfx::SurfaceFormat::R8G8B8A8, 667 gfx::YUVRangedColorSpace::GbrIdentity, {}, {}); 668 } 669 670 /* static */ ExternalTextureSourceHost 671 ExternalTextureSourceHost::CreateFromDXGITextureHost( 672 WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId, 673 const ExternalTextureSourceDescriptor& aDesc, 674 const layers::DXGITextureHostD3D11* aTextureHost) { 675 #ifdef XP_WIN 676 Maybe<HANDLE> handle; 677 if (aTextureHost->mGpuProcessTextureId) { 678 auto* textureMap = layers::GpuProcessD3D11TextureMap::Get(); 679 if (textureMap) { 680 handle = 681 textureMap->GetSharedHandle(aTextureHost->mGpuProcessTextureId.ref()); 682 } 683 } else if (aTextureHost->mHandle) { 684 handle.emplace(aTextureHost->mHandle->GetHandle()); 685 } 686 687 if (!handle) { 688 gfxCriticalErrorOnce() << "Failed to obtain D3D texture handle"; 689 aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal, 690 "Failed to obtain D3D texture handle"_ns); 691 return CreateError(); 692 } 693 694 const gfx::YUVRangedColorSpace colorSpace = gfx::ToYUVRangedColorSpace( 695 gfx::ToYUVColorSpace(aTextureHost->mColorSpace), 696 aTextureHost->mColorRange); 697 698 ffi::WGPUTextureFormat textureFormat; 699 AutoTArray<std::pair<ffi::WGPUTextureFormat, ffi::WGPUTextureAspect>, 2> 700 viewFormatAndAspects; 701 switch (aTextureHost->mFormat) { 702 case gfx::SurfaceFormat::R8G8B8A8: 703 case gfx::SurfaceFormat::R8G8B8X8: 704 textureFormat = {ffi::WGPUTextureFormat_Rgba8Unorm}; 705 viewFormatAndAspects.AppendElement( 706 std::make_pair(textureFormat, ffi::WGPUTextureAspect_All)); 707 break; 708 case gfx::SurfaceFormat::B8G8R8A8: 709 case gfx::SurfaceFormat::B8G8R8X8: 710 textureFormat = {ffi::WGPUTextureFormat_Bgra8Unorm}; 711 viewFormatAndAspects.AppendElement( 712 std::make_pair(textureFormat, ffi::WGPUTextureAspect_All)); 713 break; 714 case gfx::SurfaceFormat::NV12: 715 textureFormat = {ffi::WGPUTextureFormat_NV12}; 716 viewFormatAndAspects.AppendElement( 717 std::make_pair(ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_R8Unorm}, 718 ffi::WGPUTextureAspect_Plane0)); 719 viewFormatAndAspects.AppendElement(std::make_pair( 720 ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_Rg8Unorm}, 721 ffi::WGPUTextureAspect_Plane1)); 722 break; 723 case gfx::SurfaceFormat::P010: 724 textureFormat = {ffi::WGPUTextureFormat_P010}; 725 viewFormatAndAspects.AppendElement(std::make_pair( 726 ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_R16Unorm}, 727 ffi::WGPUTextureAspect_Plane0)); 728 viewFormatAndAspects.AppendElement(std::make_pair( 729 ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_Rg16Unorm}, 730 ffi::WGPUTextureAspect_Plane1)); 731 break; 732 default: 733 gfxCriticalNoteOnce << "Unsupported surface format: " 734 << aTextureHost->mFormat; 735 aParent->ReportError( 736 aDeviceId, dom::GPUErrorFilter::Internal, 737 nsPrintfCString("Unsupported surface format: %s", 738 mozilla::ToString(aTextureHost->mFormat).c_str())); 739 return CreateError(); 740 } 741 742 AutoTArray<RawId, 1> usedTextureIds = {aDesc.mTextureIds[0]}; 743 AutoTArray<RawId, 2> usedViewIds; 744 745 const ffi::WGPUTextureDescriptor textureDesc{ 746 .size = 747 ffi::WGPUExtent3d{ 748 .width = static_cast<uint32_t>(aTextureHost->mSize.width), 749 .height = static_cast<uint32_t>(aTextureHost->mSize.height), 750 .depth_or_array_layers = 1, 751 }, 752 .mip_level_count = 1, 753 .sample_count = 1, 754 .dimension = ffi::WGPUTextureDimension_D2, 755 .format = textureFormat, 756 .usage = WGPUTextureUsages_TEXTURE_BINDING, 757 .view_formats = {}, 758 }; 759 { 760 ErrorBuffer error; 761 ffi::wgpu_server_device_import_texture_from_shared_handle( 762 aParent->GetContext(), aDeviceId, usedTextureIds[0], &textureDesc, 763 *handle, error.ToFFI()); 764 // From here on there's no need to return early with `CreateError()` in 765 // case of an error, as an error creating a texture or view will be 766 // propagated to any views or external textures created from them. 767 // Since we have full control over the creation of this texture, any 768 // validation error we encounter should be treated as an internal error. 769 error.CoerceValidationToInternal(); 770 aParent->ForwardError(error); 771 } 772 773 for (size_t i = 0; i < viewFormatAndAspects.Length(); i++) { 774 auto [format, aspect] = viewFormatAndAspects[i]; 775 ffi::WGPUTextureViewDescriptor viewDesc{ 776 .format = &format, 777 .aspect = aspect, 778 }; 779 { 780 ErrorBuffer error; 781 ffi::wgpu_server_texture_create_view(aParent->GetContext(), aDeviceId, 782 usedTextureIds[0], aDesc.mViewIds[i], 783 &viewDesc, error.ToFFI()); 784 error.CoerceValidationToInternal(); 785 aParent->ForwardError(error); 786 } 787 usedViewIds.AppendElement(aDesc.mViewIds[i]); 788 } 789 ExternalTextureSourceHost source( 790 usedTextureIds, usedViewIds, aDesc.mSize, aTextureHost->mFormat, 791 colorSpace, aDesc.mSampleTransform, aDesc.mLoadTransform); 792 source.mFenceId = aTextureHost->mFencesHolderId; 793 return source; 794 #else 795 MOZ_CRASH(); 796 #endif 797 } 798 799 /* static */ ExternalTextureSourceHost 800 ExternalTextureSourceHost::CreateFromDXGIYCbCrTextureHost( 801 WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId, 802 const ExternalTextureSourceDescriptor& aDesc, 803 const layers::DXGIYCbCrTextureHostD3D11* aTextureHost) { 804 #ifdef XP_WIN 805 const gfx::YUVRangedColorSpace colorSpace = gfx::ToYUVRangedColorSpace( 806 aTextureHost->mYUVColorSpace, aTextureHost->mColorRange); 807 808 ffi::WGPUTextureFormat planeFormat; 809 switch (aTextureHost->mColorDepth) { 810 case gfx::ColorDepth::COLOR_8: 811 planeFormat = {ffi::WGPUTextureFormat_R8Unorm}; 812 break; 813 case gfx::ColorDepth::COLOR_10: 814 case gfx::ColorDepth::COLOR_12: 815 case gfx::ColorDepth::COLOR_16: 816 gfxCriticalNoteOnce << "Unsupported color depth: " 817 << aTextureHost->mColorDepth; 818 aParent->ReportError( 819 aDeviceId, dom::GPUErrorFilter::Internal, 820 nsPrintfCString( 821 "Unsupported color depth: %s", 822 mozilla::ToString(aTextureHost->mColorDepth).c_str())); 823 return CreateError(); 824 } 825 826 for (int i = 0; i < 3; i++) { 827 { 828 const auto size = i == 0 ? aTextureHost->mSizeY : aTextureHost->mSizeCbCr; 829 const ffi::WGPUTextureDescriptor textureDesc{ 830 .size = 831 ffi::WGPUExtent3d{ 832 .width = static_cast<uint32_t>(size.width), 833 .height = static_cast<uint32_t>(size.height), 834 .depth_or_array_layers = 1, 835 }, 836 .mip_level_count = 1, 837 .sample_count = 1, 838 .dimension = ffi::WGPUTextureDimension_D2, 839 .format = planeFormat, 840 .usage = WGPUTextureUsages_TEXTURE_BINDING, 841 .view_formats = {}, 842 }; 843 ErrorBuffer error; 844 ffi::wgpu_server_device_import_texture_from_shared_handle( 845 aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i], &textureDesc, 846 aTextureHost->mHandles[i]->GetHandle(), error.ToFFI()); 847 // From here on there's no need to return early with `CreateError()` in 848 // case of an error, as an error creating a texture or view will be 849 // propagated to any views or external textures created from them. 850 // Since we have full control over the creation of this texture, any 851 // validation error we encounter should be treated as an internal error. 852 error.CoerceValidationToInternal(); 853 aParent->ForwardError(error); 854 } 855 { 856 ffi::WGPUTextureViewDescriptor viewDesc{}; 857 ErrorBuffer error; 858 ffi::wgpu_server_texture_create_view( 859 aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i], 860 aDesc.mViewIds[i], &viewDesc, error.ToFFI()); 861 error.CoerceValidationToInternal(); 862 aParent->ForwardError(error); 863 } 864 } 865 866 ExternalTextureSourceHost source( 867 aDesc.mTextureIds, aDesc.mViewIds, aDesc.mSize, aTextureHost->GetFormat(), 868 colorSpace, aDesc.mSampleTransform, aDesc.mLoadTransform); 869 source.mFenceId = Some(aTextureHost->mFencesHolderId); 870 return source; 871 #else 872 MOZ_CRASH(); 873 #endif 874 } 875 876 /* static */ ExternalTextureSourceHost 877 ExternalTextureSourceHost::CreateFromMacIOSurfaceTextureHost( 878 WebGPUParent* aParent, RawId aDeviceId, 879 const ExternalTextureSourceDescriptor& aDesc, 880 const layers::MacIOSurfaceTextureHostOGL* aTextureHost) { 881 #ifdef XP_MACOSX 882 const RefPtr<MacIOSurface> ioSurface = aTextureHost->mSurface; 883 if (!ioSurface) { 884 gfxCriticalErrorOnce() << "Failed to lookup MacIOSurface"; 885 aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal, 886 "Failed to lookup MacIOSurface"_ns); 887 888 return CreateError(); 889 } 890 891 // mGpuFence should be null. It is only required to synchronize GPU reads 892 // from an IOSurface following GPU writes, e.g. when an IOSurface is used for 893 // WebGPU presentation. In our case the IOSurface has been written to from 894 // the CPU or obtained from a CVPixelBuffer, and no additional synchronization 895 // is required. 896 MOZ_ASSERT(!aTextureHost->mGpuFence); 897 898 const gfx::SurfaceFormat format = ioSurface->GetFormat(); 899 const gfx::YUVRangedColorSpace colorSpace = gfx::ToYUVRangedColorSpace( 900 ioSurface->GetYUVColorSpace(), ioSurface->GetColorRange()); 901 902 auto planeSize = [ioSurface](auto plane) { 903 return ffi::WGPUExtent3d{ 904 .width = static_cast<uint32_t>(ioSurface->GetDevicePixelWidth(plane)), 905 .height = static_cast<uint32_t>(ioSurface->GetDevicePixelHeight(plane)), 906 .depth_or_array_layers = 1, 907 }; 908 }; 909 auto yuvPlaneFormat = 910 [ioSurface](auto numComponents) -> ffi::WGPUTextureFormat { 911 switch (numComponents) { 912 case 1: 913 switch (ioSurface->GetColorDepth()) { 914 case gfx::ColorDepth::COLOR_8: 915 return {ffi::WGPUTextureFormat_R8Unorm}; 916 case gfx::ColorDepth::COLOR_10: 917 case gfx::ColorDepth::COLOR_12: 918 case gfx::ColorDepth::COLOR_16: 919 return {ffi::WGPUTextureFormat_R16Unorm}; 920 } 921 case 2: 922 switch (ioSurface->GetColorDepth()) { 923 case gfx::ColorDepth::COLOR_8: 924 return {ffi::WGPUTextureFormat_Rg8Unorm}; 925 case gfx::ColorDepth::COLOR_10: 926 case gfx::ColorDepth::COLOR_12: 927 case gfx::ColorDepth::COLOR_16: 928 return {ffi::WGPUTextureFormat_Rg16Unorm}; 929 } 930 default: 931 MOZ_CRASH("Invalid numComponents"); 932 } 933 }; 934 935 AutoTArray<ffi::WGPUTextureDescriptor, 2> textureDescs; 936 switch (format) { 937 case gfx::SurfaceFormat::R8G8B8A8: 938 case gfx::SurfaceFormat::R8G8B8X8: 939 textureDescs.AppendElement(ffi::WGPUTextureDescriptor{ 940 .size = planeSize(0), 941 .mip_level_count = 1, 942 .sample_count = 1, 943 .dimension = ffi::WGPUTextureDimension_D2, 944 .format = {ffi::WGPUTextureFormat_Rgba8Unorm}, 945 .usage = WGPUTextureUsages_TEXTURE_BINDING, 946 .view_formats = {}, 947 }); 948 break; 949 case gfx::SurfaceFormat::B8G8R8A8: 950 case gfx::SurfaceFormat::B8G8R8X8: 951 textureDescs.AppendElement(ffi::WGPUTextureDescriptor{ 952 .size = planeSize(0), 953 .mip_level_count = 1, 954 .sample_count = 1, 955 .dimension = ffi::WGPUTextureDimension_D2, 956 .format = {ffi::WGPUTextureFormat_Bgra8Unorm}, 957 .usage = WGPUTextureUsages_TEXTURE_BINDING, 958 .view_formats = {}, 959 }); 960 break; 961 case gfx::SurfaceFormat::NV12: 962 case gfx::SurfaceFormat::P010: { 963 textureDescs.AppendElement(ffi::WGPUTextureDescriptor{ 964 .size = planeSize(0), 965 .mip_level_count = 1, 966 .sample_count = 1, 967 .dimension = ffi::WGPUTextureDimension_D2, 968 .format = yuvPlaneFormat(1), 969 .usage = WGPUTextureUsages_TEXTURE_BINDING, 970 .view_formats = {}, 971 }); 972 textureDescs.AppendElement(ffi::WGPUTextureDescriptor{ 973 .size = planeSize(1), 974 .mip_level_count = 1, 975 .sample_count = 1, 976 .dimension = ffi::WGPUTextureDimension_D2, 977 .format = yuvPlaneFormat(2), 978 .usage = WGPUTextureUsages_TEXTURE_BINDING, 979 .view_formats = {}, 980 }); 981 } break; 982 default: 983 gfxCriticalErrorOnce() << "Unsupported IOSurface format: " << format; 984 aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal, 985 nsPrintfCString("Unsupported IOSurface format: %s", 986 mozilla::ToString(format).c_str())); 987 return CreateError(); 988 } 989 990 AutoTArray<RawId, 2> usedTextureIds; 991 AutoTArray<RawId, 2> usedViewIds; 992 for (size_t i = 0; i < textureDescs.Length(); i++) { 993 usedTextureIds.AppendElement(aDesc.mTextureIds[i]); 994 usedViewIds.AppendElement(aDesc.mViewIds[i]); 995 { 996 ErrorBuffer error; 997 ffi::wgpu_server_device_import_texture_from_iosurface( 998 aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i], 999 &textureDescs[i], ioSurface->GetIOSurfaceID(), i, error.ToFFI()); 1000 // From here on there's no need to return early with `CreateError()` in 1001 // case of an error, as an error creating a texture or view will be 1002 // propagated to any views or external textures created from them. 1003 // Since we have full control over the creation of this texture, any 1004 // validation error we encounter should be treated as an internal error. 1005 error.CoerceValidationToInternal(); 1006 aParent->ForwardError(error); 1007 } 1008 ffi::WGPUTextureViewDescriptor viewDesc{}; 1009 { 1010 ErrorBuffer error; 1011 ffi::wgpu_server_texture_create_view( 1012 aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i], 1013 aDesc.mViewIds[i], &viewDesc, error.ToFFI()); 1014 error.CoerceValidationToInternal(); 1015 aParent->ForwardError(error); 1016 } 1017 } 1018 return ExternalTextureSourceHost(usedTextureIds, usedViewIds, aDesc.mSize, 1019 format, colorSpace, aDesc.mSampleTransform, 1020 aDesc.mLoadTransform); 1021 #else 1022 MOZ_CRASH(); 1023 #endif 1024 } 1025 1026 static color::ColorspaceTransform GetColorSpaceTransform( 1027 gfx::YUVRangedColorSpace aSrcColorSpace, 1028 ffi::WGPUPredefinedColorSpace aDestColorSpace) { 1029 const bool rec709GammaAsSrgb = 1030 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb(); 1031 const bool rec2020GammaAsRec709 = 1032 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709(); 1033 1034 color::ColorspaceDesc srcColorSpace; 1035 switch (aSrcColorSpace) { 1036 case gfx::YUVRangedColorSpace::BT601_Narrow: 1037 srcColorSpace = { 1038 .chrom = color::Chromaticities::Rec601_525_Ntsc(), 1039 .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb() 1040 : color::PiecewiseGammaDesc::Rec709(), 1041 .yuv = 1042 color::YuvDesc{ 1043 .yCoeffs = color::YuvLumaCoeffs::Rec601(), 1044 .ycbcr = color::YcbcrDesc::Narrow8(), 1045 }, 1046 }; 1047 break; 1048 case gfx::YUVRangedColorSpace::BT601_Full: 1049 srcColorSpace = { 1050 .chrom = color::Chromaticities::Rec601_525_Ntsc(), 1051 .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb() 1052 : color::PiecewiseGammaDesc::Rec709(), 1053 .yuv = 1054 color::YuvDesc{ 1055 .yCoeffs = color::YuvLumaCoeffs::Rec601(), 1056 .ycbcr = color::YcbcrDesc::Full8(), 1057 }, 1058 }; 1059 break; 1060 case gfx::YUVRangedColorSpace::BT709_Narrow: 1061 srcColorSpace = { 1062 .chrom = color::Chromaticities::Rec709(), 1063 .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb() 1064 : color::PiecewiseGammaDesc::Rec709(), 1065 .yuv = 1066 color::YuvDesc{ 1067 .yCoeffs = color::YuvLumaCoeffs::Rec709(), 1068 .ycbcr = color::YcbcrDesc::Narrow8(), 1069 }, 1070 }; 1071 break; 1072 case gfx::YUVRangedColorSpace::BT709_Full: 1073 srcColorSpace = { 1074 .chrom = color::Chromaticities::Rec709(), 1075 .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb() 1076 : color::PiecewiseGammaDesc::Rec709(), 1077 .yuv = 1078 color::YuvDesc{ 1079 .yCoeffs = color::YuvLumaCoeffs::Rec709(), 1080 .ycbcr = color::YcbcrDesc::Full8(), 1081 }, 1082 }; 1083 break; 1084 case gfx::YUVRangedColorSpace::BT2020_Narrow: 1085 srcColorSpace = { 1086 .chrom = color::Chromaticities::Rec2020(), 1087 .tf = rec2020GammaAsRec709 && rec709GammaAsSrgb 1088 ? color::PiecewiseGammaDesc::Srgb() 1089 : (rec2020GammaAsRec709 1090 ? color::PiecewiseGammaDesc::Rec709() 1091 : color::PiecewiseGammaDesc::Rec2020_12bit()), 1092 .yuv = 1093 color::YuvDesc{ 1094 .yCoeffs = color::YuvLumaCoeffs::Rec2020(), 1095 .ycbcr = color::YcbcrDesc::Narrow8(), 1096 }, 1097 }; 1098 break; 1099 case gfx::YUVRangedColorSpace::BT2020_Full: 1100 srcColorSpace = { 1101 .chrom = color::Chromaticities::Rec2020(), 1102 .tf = rec2020GammaAsRec709 && rec709GammaAsSrgb 1103 ? color::PiecewiseGammaDesc::Srgb() 1104 : (rec2020GammaAsRec709 1105 ? color::PiecewiseGammaDesc::Rec709() 1106 : color::PiecewiseGammaDesc::Rec2020_12bit()), 1107 .yuv = 1108 color::YuvDesc{ 1109 .yCoeffs = color::YuvLumaCoeffs::Rec2020(), 1110 .ycbcr = color::YcbcrDesc::Full8(), 1111 }, 1112 }; 1113 break; 1114 case gfx::YUVRangedColorSpace::GbrIdentity: 1115 srcColorSpace = { 1116 .chrom = color::Chromaticities::Rec709(), 1117 .tf = color::PiecewiseGammaDesc::Rec709(), 1118 .yuv = 1119 color::YuvDesc{ 1120 .yCoeffs = color::YuvLumaCoeffs::Gbr(), 1121 .ycbcr = color::YcbcrDesc::Full8(), 1122 }, 1123 }; 1124 break; 1125 } 1126 1127 color::ColorspaceDesc destColorSpace{}; 1128 switch (aDestColorSpace) { 1129 case ffi::WGPUPredefinedColorSpace_Srgb: 1130 destColorSpace = {.chrom = color::Chromaticities::Srgb(), 1131 .tf = color::PiecewiseGammaDesc::Srgb()}; 1132 break; 1133 case ffi::WGPUPredefinedColorSpace_DisplayP3: 1134 destColorSpace = {.chrom = color::Chromaticities::DisplayP3(), 1135 .tf = color::PiecewiseGammaDesc::DisplayP3()}; 1136 break; 1137 case ffi::WGPUPredefinedColorSpace_Sentinel: 1138 MOZ_CRASH("Invalid WGPUPredefinedColorSpace"); 1139 } 1140 1141 return color::ColorspaceTransform::Create(srcColorSpace, destColorSpace); 1142 } 1143 1144 static ffi::WGPUExternalTextureFormat MapFormat(gfx::SurfaceFormat aFormat) { 1145 switch (aFormat) { 1146 case gfx::SurfaceFormat::B8G8R8A8: 1147 case gfx::SurfaceFormat::B8G8R8X8: 1148 case gfx::SurfaceFormat::R8G8B8A8: 1149 case gfx::SurfaceFormat::R8G8B8X8: 1150 return ffi::WGPUExternalTextureFormat_Rgba; 1151 case gfx::SurfaceFormat::YUV420: 1152 return ffi::WGPUExternalTextureFormat_Yu12; 1153 case gfx::SurfaceFormat::NV12: 1154 case gfx::SurfaceFormat::P010: 1155 return ffi::WGPUExternalTextureFormat_Nv12; 1156 default: 1157 MOZ_CRASH("Unexpected SurfaceFormat"); 1158 } 1159 } 1160 1161 static ffi::WGPUExternalTextureTransferFunction MapTransferFunction( 1162 std::optional<color::PiecewiseGammaDesc> aTf) { 1163 if (aTf) { 1164 return ffi::WGPUExternalTextureTransferFunction{ 1165 .a = aTf->a, 1166 .b = aTf->b, 1167 .g = aTf->g, 1168 .k = aTf->k, 1169 }; 1170 } else { 1171 return ffi::WGPUExternalTextureTransferFunction{ 1172 .a = 1.0, 1173 .b = 1.0, 1174 .g = 1.0, 1175 .k = 1.0, 1176 }; 1177 } 1178 } 1179 1180 ffi::WGPUExternalTextureDescriptorFromSource 1181 ExternalTextureSourceHost::GetExternalTextureDescriptor( 1182 ffi::WGPUPredefinedColorSpace aDestColorSpace) const { 1183 ffi::WGPUExternalTextureDescriptorFromSource desc; 1184 1185 desc.planes = ffi::WGPUFfiSlice_TextureViewId{ 1186 .data = mViewIds.Elements(), 1187 .length = mViewIds.Length(), 1188 }; 1189 desc.width = static_cast<uint32_t>(mSize.width); 1190 desc.height = static_cast<uint32_t>(mSize.height); 1191 desc.format = MapFormat(mFormat); 1192 1193 auto colorSpaceTransform = 1194 GetColorSpaceTransform(mColorSpace, aDestColorSpace); 1195 // Makes a generator for a color::mat that yields its components in 1196 // column-major order 1197 auto make_column_major_generator = [](auto mat) { 1198 return [i = 0, mat]() mutable { 1199 auto val = mat.at(i / mat.y_rows, i % mat.y_rows); 1200 i++; 1201 return val; 1202 }; 1203 }; 1204 std::generate( 1205 std::begin(desc.yuv_conversion_matrix), 1206 std::end(desc.yuv_conversion_matrix), 1207 make_column_major_generator(colorSpaceTransform.srcRgbTfFromSrc)); 1208 std::generate( 1209 std::begin(desc.gamut_conversion_matrix), 1210 std::end(desc.gamut_conversion_matrix), 1211 make_column_major_generator(colorSpaceTransform.dstRgbLinFromSrcRgbLin)); 1212 desc.src_transfer_function = MapTransferFunction(colorSpaceTransform.srcTf); 1213 desc.dst_transfer_function = MapTransferFunction(colorSpaceTransform.dstTf); 1214 std::copy(mSampleTransform.begin(), mSampleTransform.end(), 1215 desc.sample_transform); 1216 std::copy(mLoadTransform.begin(), mLoadTransform.end(), desc.load_transform); 1217 1218 return desc; 1219 } 1220 1221 bool ExternalTextureSourceHost::OnBeforeQueueSubmit(WebGPUParent* aParent, 1222 RawId aDeviceId, 1223 RawId aQueueId) { 1224 #if defined(XP_WIN) 1225 // Wait on the write fence provided by the decoder, if any, to ensure we don't 1226 // read from the texture before writes have completed. 1227 if (mFenceId) { 1228 const auto* fencesMap = layers::CompositeProcessD3D11FencesHolderMap::Get(); 1229 if (!fencesMap) { 1230 gfxCriticalErrorOnce() 1231 << "CompositeProcessD3D11FencesHolderMap is not initialized"; 1232 aParent->ReportError( 1233 aDeviceId, dom::GPUErrorFilter::Internal, 1234 "CompositeProcessD3D11FencesHolderMap is not initialized"_ns); 1235 return false; 1236 } 1237 auto [fenceHandle, fenceValue] = 1238 fencesMap->GetWriteFenceHandleAndValue(*mFenceId); 1239 if (fenceHandle) { 1240 const bool success = 1241 ffi::wgpu_server_device_wait_fence_from_shared_handle( 1242 aParent->GetContext(), aDeviceId, aQueueId, 1243 fenceHandle->GetHandle(), fenceValue); 1244 if (success) { 1245 // No need to wait next time 1246 mFenceId.reset(); 1247 } else { 1248 gfxCriticalErrorOnce() << "Failed to wait on write fence"; 1249 aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal, 1250 "Failed to wait on write fence"_ns); 1251 return false; 1252 } 1253 } 1254 } 1255 return true; 1256 #else 1257 return true; 1258 #endif 1259 } 1260 1261 } // namespace mozilla::webgpu