| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/webgpu/gpu_device.h" |
| |
| #include "gpu/command_buffer/client/webgpu_interface.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_compute_pipeline_descriptor.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_device_descriptor.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_error_filter.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_feature_name.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_query_set_descriptor.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_queue_descriptor.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pipeline_descriptor.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_uncaptured_error_event_init.h" |
| #include "third_party/blink/renderer/core/dom/dom_exception.h" |
| #include "third_party/blink/renderer/core/inspector/console_message.h" |
| #include "third_party/blink/renderer/modules/event_target_modules.h" |
| #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_bind_group.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_compute_pipeline.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_device_lost_info.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_external_texture.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_internal_error.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_out_of_memory_error.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_pipeline_error.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_query_set.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_queue.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_sampler.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_shader_module.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_supported_features.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_supported_limits.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_texture.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event.h" |
| #include "third_party/blink/renderer/modules/webgpu/gpu_validation_error.h" |
| #include "third_party/blink/renderer/modules/webgpu/string_utils.h" |
| #include "third_party/blink/renderer/platform/heap/thread_state.h" |
| #include "third_party/blink/renderer/platform/wtf/text/strcat.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| std::optional<V8GPUFeatureName::Enum> RequiredFeatureForTextureFormat( |
| V8GPUTextureFormat::Enum format) { |
| switch (format) { |
| case V8GPUTextureFormat::Enum::kBc1RgbaUnorm: |
| case V8GPUTextureFormat::Enum::kBc1RgbaUnormSrgb: |
| case V8GPUTextureFormat::Enum::kBc2RgbaUnorm: |
| case V8GPUTextureFormat::Enum::kBc2RgbaUnormSrgb: |
| case V8GPUTextureFormat::Enum::kBc3RgbaUnorm: |
| case V8GPUTextureFormat::Enum::kBc3RgbaUnormSrgb: |
| case V8GPUTextureFormat::Enum::kBc4RUnorm: |
| case V8GPUTextureFormat::Enum::kBc4RSnorm: |
| case V8GPUTextureFormat::Enum::kBc5RgUnorm: |
| case V8GPUTextureFormat::Enum::kBc5RgSnorm: |
| case V8GPUTextureFormat::Enum::kBc6HRgbUfloat: |
| case V8GPUTextureFormat::Enum::kBc6HRgbFloat: |
| case V8GPUTextureFormat::Enum::kBc7RgbaUnorm: |
| case V8GPUTextureFormat::Enum::kBc7RgbaUnormSrgb: |
| return V8GPUFeatureName::Enum::kTextureCompressionBc; |
| |
| case V8GPUTextureFormat::Enum::kEtc2Rgb8Unorm: |
| case V8GPUTextureFormat::Enum::kEtc2Rgb8UnormSrgb: |
| case V8GPUTextureFormat::Enum::kEtc2Rgb8A1Unorm: |
| case V8GPUTextureFormat::Enum::kEtc2Rgb8A1UnormSrgb: |
| case V8GPUTextureFormat::Enum::kEtc2Rgba8Unorm: |
| case V8GPUTextureFormat::Enum::kEtc2Rgba8UnormSrgb: |
| case V8GPUTextureFormat::Enum::kEacR11Unorm: |
| case V8GPUTextureFormat::Enum::kEacR11Snorm: |
| case V8GPUTextureFormat::Enum::kEacRg11Unorm: |
| case V8GPUTextureFormat::Enum::kEacRg11Snorm: |
| return V8GPUFeatureName::Enum::kTextureCompressionEtc2; |
| |
| case V8GPUTextureFormat::Enum::kAstc4X4Unorm: |
| case V8GPUTextureFormat::Enum::kAstc4X4UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc5X4Unorm: |
| case V8GPUTextureFormat::Enum::kAstc5X4UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc5X5Unorm: |
| case V8GPUTextureFormat::Enum::kAstc5X5UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc6X5Unorm: |
| case V8GPUTextureFormat::Enum::kAstc6X5UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc6X6Unorm: |
| case V8GPUTextureFormat::Enum::kAstc6X6UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc8X5Unorm: |
| case V8GPUTextureFormat::Enum::kAstc8X5UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc8X6Unorm: |
| case V8GPUTextureFormat::Enum::kAstc8X6UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc8X8Unorm: |
| case V8GPUTextureFormat::Enum::kAstc8X8UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc10X5Unorm: |
| case V8GPUTextureFormat::Enum::kAstc10X5UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc10X6Unorm: |
| case V8GPUTextureFormat::Enum::kAstc10X6UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc10X8Unorm: |
| case V8GPUTextureFormat::Enum::kAstc10X8UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc10X10Unorm: |
| case V8GPUTextureFormat::Enum::kAstc10X10UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc12X10Unorm: |
| case V8GPUTextureFormat::Enum::kAstc12X10UnormSrgb: |
| case V8GPUTextureFormat::Enum::kAstc12X12Unorm: |
| case V8GPUTextureFormat::Enum::kAstc12X12UnormSrgb: |
| return V8GPUFeatureName::Enum::kTextureCompressionAstc; |
| |
| case V8GPUTextureFormat::Enum::kDepth32FloatStencil8: |
| return V8GPUFeatureName::Enum::kDepth32FloatStencil8; |
| |
| case V8GPUTextureFormat::Enum::kR16Unorm: |
| case V8GPUTextureFormat::Enum::kRg16Unorm: |
| case V8GPUTextureFormat::Enum::kRgba16Unorm: |
| case V8GPUTextureFormat::Enum::kR16Snorm: |
| case V8GPUTextureFormat::Enum::kRg16Snorm: |
| case V8GPUTextureFormat::Enum::kRgba16Snorm: |
| return V8GPUFeatureName::Enum::kTextureFormatsTier1; |
| |
| default: |
| return std::nullopt; |
| } |
| } |
| |
| std::optional<V8GPUFeatureName::Enum> RequiredFeatureForBlendFactor( |
| V8GPUBlendFactor::Enum blend_factor) { |
| switch (blend_factor) { |
| case V8GPUBlendFactor::Enum::kSrc1: |
| case V8GPUBlendFactor::Enum::kOneMinusSrc1: |
| case V8GPUBlendFactor::Enum::kSrc1Alpha: |
| case V8GPUBlendFactor::Enum::kOneMinusSrc1Alpha: |
| return V8GPUFeatureName::Enum::kDualSourceBlending; |
| default: |
| return std::nullopt; |
| } |
| } |
| |
| } // anonymous namespace |
| |
| GPUDevice::GPUDevice(ExecutionContext* execution_context, |
| scoped_refptr<DawnControlClientHolder> dawn_control_client, |
| GPUAdapter* adapter, |
| const String& label) |
| : ExecutionContextClient(execution_context), |
| DawnObject(dawn_control_client, label), |
| adapter_(adapter), |
| lost_property_(MakeGarbageCollected<LostProperty>(execution_context)), |
| logging_callback_(BindWGPURepeatingCallback(&GPUDevice::OnLogging, |
| WrapWeakPersistent(this))) {} |
| |
| void GPUDevice::Initialize(wgpu::Device handle, |
| const GPUDeviceDescriptor* descriptor, |
| GPUDeviceLostInfo* lost_info) { |
| SetHandle(std::move(handle)); |
| |
| wgpu::SupportedFeatures features; |
| GetHandle().GetFeatures(&features); |
| features_ = MakeGarbageCollected<GPUSupportedFeatures>(features); |
| |
| queue_ = MakeGarbageCollected<GPUQueue>(this, GetHandle().GetQueue(), |
| descriptor->defaultQueue()->label()); |
| |
| GPUSupportedLimits::ComboLimits limits; |
| GetHandle().GetLimits(limits.GetLinked()); |
| limits_ = MakeGarbageCollected<GPUSupportedLimits>(limits); |
| |
| adapter_info_ = adapter_->CreateAdapterInfoForAdapter(); |
| |
| GetHandle().SetLoggingCallback(logging_callback_->UnboundCallback(), |
| logging_callback_->AsUserdata()); |
| |
| external_texture_cache_ = MakeGarbageCollected<ExternalTextureCache>(this); |
| |
| // If lost_info is supplied it means the device should be treated as being |
| // lost at creation time. |
| if (lost_info) { |
| lost_property_->Resolve(lost_info); |
| } |
| } |
| |
| GPUDevice::~GPUDevice() { |
| // Perform destruction that's safe to do inside a GC (as in it doesn't touch |
| // other GC objects). |
| |
| // Clear the callbacks since we can't handle callbacks after finalization. |
| if (GetHandle().Get() != nullptr) { |
| GetHandle().SetLoggingCallback([](wgpu::LoggingType, wgpu::StringView) {}); |
| } |
| } |
| |
| bool GPUDevice::IsDestroyed() const { |
| return destroyed_; |
| } |
| |
| std::string GPUDevice::GetFormattedLabel() const { |
| std::string deviceLabel = |
| label().empty() ? "[Device]" : "[Device \"" + label().Utf8() + "\"]"; |
| |
| return deviceLabel; |
| } |
| |
| void GPUDevice::InjectError(wgpu::ErrorType type, const char* message) { |
| GetHandle().InjectError(type, message); |
| } |
| |
| void GPUDevice::AddConsoleWarning(wgpu::StringView message) { |
| AddConsoleWarning(StringFromASCIIAndUTF8(message)); |
| } |
| |
| void GPUDevice::AddConsoleWarning(const char* message) { |
| AddConsoleWarning(StringFromASCIIAndUTF8(message)); |
| } |
| |
| void GPUDevice::AddConsoleWarning(const String& message) { |
| ExecutionContext* execution_context = GetExecutionContext(); |
| if (execution_context && allowed_console_warnings_remaining_ > 0) { |
| auto* console_message = MakeGarbageCollected<ConsoleMessage>( |
| mojom::blink::ConsoleMessageSource::kRendering, |
| mojom::blink::ConsoleMessageLevel::kWarning, message); |
| execution_context->AddConsoleMessage(console_message); |
| |
| allowed_console_warnings_remaining_--; |
| if (allowed_console_warnings_remaining_ == 0) { |
| auto* final_message = MakeGarbageCollected<ConsoleMessage>( |
| mojom::blink::ConsoleMessageSource::kRendering, |
| mojom::blink::ConsoleMessageLevel::kWarning, |
| "WebGPU: too many warnings, no more warnings will be reported to the " |
| "console for this GPUDevice."); |
| execution_context->AddConsoleMessage(final_message); |
| } |
| } |
| } |
| |
| void GPUDevice::AddSingletonWarning(GPUSingletonWarning type) { |
| size_t index = static_cast<size_t>(type); |
| if (!singleton_warning_fired_[index]) [[unlikely]] { |
| singleton_warning_fired_[index] = true; |
| |
| String message; |
| switch (type) { |
| case GPUSingletonWarning::kNonPreferredFormat: |
| message = StrCat( |
| {"WebGPU canvas configured with a different format than is " |
| "preferred by this device (\"", |
| FromDawnEnum(GPU::GetPreferredCanvasFormat()).AsStringView(), |
| "\"). This requires an extra copy, which may impact " |
| "performance."}); |
| break; |
| case GPUSingletonWarning::kDepthKey: |
| message = |
| "The key \"depth\" was included in a GPUExtent3D dictionary, which " |
| "has no effect. It is likely that \"depthOrArrayLayers\" was " |
| "intended instead."; |
| break; |
| case GPUSingletonWarning::kCount: |
| NOTREACHED(); |
| } |
| |
| ExecutionContext* execution_context = GetExecutionContext(); |
| if (execution_context) { |
| auto* console_message = MakeGarbageCollected<ConsoleMessage>( |
| mojom::blink::ConsoleMessageSource::kRendering, |
| mojom::blink::ConsoleMessageLevel::kWarning, message); |
| execution_context->AddConsoleMessage(console_message); |
| } |
| } |
| } |
| |
| // Validates that any features required for the given texture format are enabled |
| // for this device. If not, throw a TypeError to ensure consistency with |
| // browsers that haven't yet implemented the feature. |
| bool GPUDevice::ValidateTextureFormatUsage(V8GPUTextureFormat format, |
| ExceptionState& exception_state) { |
| auto requiredFeatureOptional = |
| RequiredFeatureForTextureFormat(format.AsEnum()); |
| |
| if (!requiredFeatureOptional) { |
| return true; |
| } |
| |
| V8GPUFeatureName::Enum requiredFeatureEnum = requiredFeatureOptional.value(); |
| |
| if (features_->Has(requiredFeatureEnum)) { |
| return true; |
| } |
| |
| V8GPUFeatureName requiredFeature = V8GPUFeatureName(requiredFeatureEnum); |
| |
| exception_state.ThrowTypeError(String::Format( |
| "Use of the '%s' texture format requires the '%s' feature " |
| "to be enabled on %s.", |
| format.AsCStr(), requiredFeature.AsCStr(), GetFormattedLabel().c_str())); |
| return false; |
| } |
| |
| // Validates that any features required for the given blend factor are enabled |
| // for this device. If not, throw a TypeError to ensure consistency with |
| // browsers that haven't yet implemented the feature. |
| bool GPUDevice::ValidateBlendFactor(V8GPUBlendFactor blend_factor, |
| ExceptionState& exception_state) { |
| auto requiredFeatureOptional = |
| RequiredFeatureForBlendFactor(blend_factor.AsEnum()); |
| |
| if (!requiredFeatureOptional) { |
| return true; |
| } |
| |
| V8GPUFeatureName::Enum requiredFeatureEnum = requiredFeatureOptional.value(); |
| |
| if (features_->Has(requiredFeatureEnum)) { |
| return true; |
| } |
| |
| V8GPUFeatureName requiredFeature = V8GPUFeatureName(requiredFeatureEnum); |
| |
| exception_state.ThrowTypeError( |
| String::Format("Use of the '%s' blend factor requires the '%s' feature " |
| "to be enabled on %s.", |
| blend_factor.AsCStr(), requiredFeature.AsCStr(), |
| GetFormattedLabel().c_str())); |
| return false; |
| } |
| |
| void GPUDevice::OnUncapturedError(const wgpu::Device& device, |
| wgpu::ErrorType errorType, |
| wgpu::StringView message) { |
| // Suppress errors once the device is lost. |
| if (lost_property_->GetState() == LostProperty::kResolved) { |
| return; |
| } |
| |
| DCHECK_NE(errorType, wgpu::ErrorType::NoError); |
| LOG(ERROR) << "GPUDevice: " << std::string_view(message); |
| |
| GPUUncapturedErrorEventInit* init = GPUUncapturedErrorEventInit::Create(); |
| if (errorType == wgpu::ErrorType::Validation) { |
| init->setError(MakeGarbageCollected<GPUValidationError>( |
| StringFromASCIIAndUTF8(message))); |
| } else if (errorType == wgpu::ErrorType::OutOfMemory) { |
| init->setError(MakeGarbageCollected<GPUOutOfMemoryError>( |
| StringFromASCIIAndUTF8(message))); |
| } else if (errorType == wgpu::ErrorType::Internal) { |
| init->setError(MakeGarbageCollected<GPUInternalError>( |
| StringFromASCIIAndUTF8(message))); |
| } else { |
| return; |
| } |
| |
| GPUUncapturedErrorEvent* event = |
| GPUUncapturedErrorEvent::Create(event_type_names::kUncapturederror, init); |
| DispatchEvent(*event); |
| if (!event->defaultPrevented()) { |
| AddConsoleWarning(message); |
| } |
| } |
| |
| void GPUDevice::OnLogging(wgpu::LoggingType loggingType, |
| wgpu::StringView message) { |
| std::string_view messageView = {message.data, message.length}; |
| // Callback function for WebGPU logging return command |
| mojom::blink::ConsoleMessageLevel level; |
| switch (loggingType) { |
| case (wgpu::LoggingType::Verbose): { |
| level = mojom::blink::ConsoleMessageLevel::kVerbose; |
| break; |
| } |
| case (wgpu::LoggingType::Info): { |
| level = mojom::blink::ConsoleMessageLevel::kInfo; |
| break; |
| } |
| case (wgpu::LoggingType::Warning): { |
| level = mojom::blink::ConsoleMessageLevel::kWarning; |
| break; |
| } |
| case (wgpu::LoggingType::Error): { |
| level = mojom::blink::ConsoleMessageLevel::kError; |
| break; |
| } |
| default: { |
| level = mojom::blink::ConsoleMessageLevel::kError; |
| break; |
| } |
| } |
| ExecutionContext* execution_context = GetExecutionContext(); |
| if (execution_context) { |
| auto* console_message = MakeGarbageCollected<ConsoleMessage>( |
| mojom::blink::ConsoleMessageSource::kRendering, level, |
| StringFromASCIIAndUTF8(messageView)); |
| execution_context->AddConsoleMessage(console_message); |
| } |
| } |
| |
| void GPUDevice::OnDeviceLost( |
| std::unique_ptr<WGPURepeatingCallback<wgpu::UncapturedErrorCallback<void>>>, |
| const wgpu::Device& device, |
| wgpu::DeviceLostReason reason, |
| wgpu::StringView message) { |
| // Early-out if the context is being destroyed (see WrapCallbackInScriptScope) |
| if (!GetExecutionContext()) { |
| return; |
| } |
| |
| if (reason != wgpu::DeviceLostReason::Destroyed) { |
| AddConsoleWarning(message); |
| } |
| |
| if (lost_property_->GetState() == LostProperty::kPending) { |
| auto* device_lost_info = MakeGarbageCollected<GPUDeviceLostInfo>( |
| reason, StringFromASCIIAndUTF8(message)); |
| lost_property_->Resolve(device_lost_info); |
| } |
| } |
| |
| void GPUDevice::OnCreateRenderPipelineAsyncCallback( |
| const String& label, |
| ScriptPromiseResolver<GPURenderPipeline>* resolver, |
| wgpu::CreatePipelineAsyncStatus status, |
| wgpu::RenderPipeline render_pipeline, |
| wgpu::StringView message) { |
| ScriptState* script_state = resolver->GetScriptState(); |
| switch (status) { |
| case wgpu::CreatePipelineAsyncStatus::Success: { |
| GPURenderPipeline* pipeline = MakeGarbageCollected<GPURenderPipeline>( |
| this, std::move(render_pipeline), label); |
| resolver->Resolve(pipeline); |
| break; |
| } |
| |
| case wgpu::CreatePipelineAsyncStatus::ValidationError: { |
| resolver->Reject(GPUPipelineError::Create( |
| script_state->GetIsolate(), StringFromASCIIAndUTF8(message), |
| V8GPUPipelineErrorReason::Enum::kValidation)); |
| break; |
| } |
| |
| case wgpu::CreatePipelineAsyncStatus::InternalError: |
| case wgpu::CreatePipelineAsyncStatus::CallbackCancelled: { |
| resolver->Reject(GPUPipelineError::Create( |
| script_state->GetIsolate(), StringFromASCIIAndUTF8(message), |
| V8GPUPipelineErrorReason::Enum::kInternal)); |
| break; |
| } |
| } |
| } |
| |
| void GPUDevice::OnCreateComputePipelineAsyncCallback( |
| const String& label, |
| ScriptPromiseResolver<GPUComputePipeline>* resolver, |
| wgpu::CreatePipelineAsyncStatus status, |
| wgpu::ComputePipeline compute_pipeline, |
| wgpu::StringView message) { |
| ScriptState* script_state = resolver->GetScriptState(); |
| switch (status) { |
| case wgpu::CreatePipelineAsyncStatus::Success: { |
| GPUComputePipeline* pipeline = MakeGarbageCollected<GPUComputePipeline>( |
| this, std::move(compute_pipeline), label); |
| resolver->Resolve(pipeline); |
| break; |
| } |
| |
| case wgpu::CreatePipelineAsyncStatus::ValidationError: { |
| resolver->Reject(GPUPipelineError::Create( |
| script_state->GetIsolate(), StringFromASCIIAndUTF8(message), |
| V8GPUPipelineErrorReason::Enum::kValidation)); |
| break; |
| } |
| |
| case wgpu::CreatePipelineAsyncStatus::InternalError: |
| case wgpu::CreatePipelineAsyncStatus::CallbackCancelled: { |
| resolver->Reject(GPUPipelineError::Create( |
| script_state->GetIsolate(), StringFromASCIIAndUTF8(message), |
| V8GPUPipelineErrorReason::Enum::kInternal)); |
| break; |
| } |
| } |
| } |
| |
| GPUAdapter* GPUDevice::adapter() const { |
| return adapter_.Get(); |
| } |
| |
| GPUSupportedFeatures* GPUDevice::features() const { |
| return features_.Get(); |
| } |
| |
| GPUAdapterInfo* GPUDevice::adapterInfo() const { |
| return adapter_info_.Get(); |
| } |
| |
| ScriptPromise<GPUDeviceLostInfo> GPUDevice::lost(ScriptState* script_state) { |
| return lost_property_->Promise(script_state->World()); |
| } |
| |
| GPUQueue* GPUDevice::queue() { |
| return queue_.Get(); |
| } |
| |
| void GPUDevice::destroy(v8::Isolate* isolate) { |
| destroyed_ = true; |
| external_texture_cache_->Destroy(); |
| // Dissociate mailboxes before destroying the device. This ensures that |
| // mailbox operations which run during dissociation can succeed. |
| DissociateMailboxes(); |
| UnmapAllMappableBuffers(isolate); |
| GetHandle().Destroy(); |
| FlushNow(); |
| } |
| |
| GPUBuffer* GPUDevice::createBuffer(const GPUBufferDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| return GPUBuffer::Create(this, descriptor, exception_state); |
| } |
| |
| GPUTexture* GPUDevice::createTexture(const GPUTextureDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| return GPUTexture::Create(this, descriptor, exception_state); |
| } |
| |
| GPUSampler* GPUDevice::createSampler(const GPUSamplerDescriptor* descriptor) { |
| return GPUSampler::Create(this, descriptor); |
| } |
| |
| GPUExternalTexture* GPUDevice::importExternalTexture( |
| const GPUExternalTextureDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| return external_texture_cache_->Import(descriptor, exception_state); |
| } |
| |
| GPUBindGroup* GPUDevice::createBindGroup( |
| const GPUBindGroupDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| return GPUBindGroup::Create(this, descriptor, exception_state); |
| } |
| |
| GPUBindGroupLayout* GPUDevice::createBindGroupLayout( |
| const GPUBindGroupLayoutDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| return GPUBindGroupLayout::Create(this, descriptor, exception_state); |
| } |
| |
| GPUPipelineLayout* GPUDevice::createPipelineLayout( |
| const GPUPipelineLayoutDescriptor* descriptor) { |
| return GPUPipelineLayout::Create(this, descriptor); |
| } |
| |
| GPUShaderModule* GPUDevice::createShaderModule( |
| const GPUShaderModuleDescriptor* descriptor) { |
| return GPUShaderModule::Create(this, descriptor); |
| } |
| |
| GPURenderPipeline* GPUDevice::createRenderPipeline( |
| ScriptState* script_state, |
| const GPURenderPipelineDescriptor* descriptor) { |
| return GPURenderPipeline::Create(script_state, this, descriptor); |
| } |
| |
| GPUComputePipeline* GPUDevice::createComputePipeline( |
| const GPUComputePipelineDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| return GPUComputePipeline::Create(this, descriptor); |
| } |
| |
| ScriptPromise<GPURenderPipeline> GPUDevice::createRenderPipelineAsync( |
| ScriptState* script_state, |
| const GPURenderPipelineDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| OwnedRenderPipelineDescriptor dawn_desc_info; |
| ConvertToDawnType(script_state->GetIsolate(), this, descriptor, |
| &dawn_desc_info, exception_state); |
| if (exception_state.HadException()) { |
| return EmptyPromise(); |
| } |
| |
| auto* resolver = |
| MakeGarbageCollected<ScriptPromiseResolver<GPURenderPipeline>>( |
| script_state, exception_state.GetContext()); |
| auto promise = resolver->Promise(); |
| auto* callback = MakeWGPUOnceCallback(resolver->WrapCallbackInScriptScope( |
| BindOnce(&GPUDevice::OnCreateRenderPipelineAsyncCallback, |
| WrapPersistent(this), descriptor->label()))); |
| |
| GetHandle().CreateRenderPipelineAsync( |
| &dawn_desc_info.dawn_desc, wgpu::CallbackMode::AllowSpontaneous, |
| callback->UnboundCallback(), callback->AsUserdata()); |
| |
| // WebGPU guarantees that promises are resolved in finite time so we need to |
| // ensure commands are flushed. |
| EnsureFlush(ToEventLoop(script_state)); |
| return promise; |
| } |
| |
| ScriptPromise<GPUComputePipeline> GPUDevice::createComputePipelineAsync( |
| ScriptState* script_state, |
| const GPUComputePipelineDescriptor* descriptor) { |
| auto* resolver = |
| MakeGarbageCollected<ScriptPromiseResolver<GPUComputePipeline>>( |
| script_state); |
| auto promise = resolver->Promise(); |
| |
| std::string desc_label; |
| OwnedProgrammableStage computeStage; |
| wgpu::ComputePipelineDescriptor dawn_desc = |
| AsDawnType(this, descriptor, &desc_label, &computeStage); |
| |
| auto* callback = MakeWGPUOnceCallback(resolver->WrapCallbackInScriptScope( |
| BindOnce(&GPUDevice::OnCreateComputePipelineAsyncCallback, |
| WrapPersistent(this), descriptor->label()))); |
| |
| GetHandle().CreateComputePipelineAsync( |
| &dawn_desc, wgpu::CallbackMode::AllowSpontaneous, |
| callback->UnboundCallback(), callback->AsUserdata()); |
| // WebGPU guarantees that promises are resolved in finite time so we need to |
| // ensure commands are flushed. |
| EnsureFlush(ToEventLoop(script_state)); |
| return promise; |
| } |
| |
| GPUCommandEncoder* GPUDevice::createCommandEncoder( |
| const GPUCommandEncoderDescriptor* descriptor) { |
| return GPUCommandEncoder::Create(this, descriptor); |
| } |
| |
| GPURenderBundleEncoder* GPUDevice::createRenderBundleEncoder( |
| const GPURenderBundleEncoderDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| return GPURenderBundleEncoder::Create(this, descriptor, exception_state); |
| } |
| |
| GPUQuerySet* GPUDevice::createQuerySet(const GPUQuerySetDescriptor* descriptor, |
| ExceptionState& exception_state) { |
| const V8GPUFeatureName::Enum kTimestampQuery = |
| V8GPUFeatureName::Enum::kTimestampQuery; |
| const V8GPUFeatureName::Enum kTimestampQueryInsidePasses = |
| V8GPUFeatureName::Enum::kChromiumExperimentalTimestampQueryInsidePasses; |
| if (descriptor->type() == V8GPUQueryType::Enum::kTimestamp && |
| !features_->Has(kTimestampQuery) && |
| !features_->Has(kTimestampQueryInsidePasses)) { |
| exception_state.ThrowTypeError( |
| String::Format("Use of timestamp queries requires the '%s' or '%s' " |
| "feature to be enabled on %s.", |
| V8GPUFeatureName(kTimestampQuery).AsCStr(), |
| V8GPUFeatureName(kTimestampQueryInsidePasses).AsCStr(), |
| GetFormattedLabel().c_str())); |
| return nullptr; |
| } |
| return GPUQuerySet::Create(this, descriptor); |
| } |
| |
| void GPUDevice::pushErrorScope(const V8GPUErrorFilter& filter) { |
| GetHandle().PushErrorScope(AsDawnEnum(filter)); |
| } |
| |
| ScriptPromise<IDLNullable<GPUError>> GPUDevice::popErrorScope( |
| ScriptState* script_state) { |
| auto* resolver = |
| MakeGarbageCollected<ScriptPromiseResolver<IDLNullable<GPUError>>>( |
| script_state); |
| auto promise = resolver->Promise(); |
| |
| auto* callback = MakeWGPUOnceCallback(resolver->WrapCallbackInScriptScope( |
| BindOnce(&GPUDevice::OnPopErrorScopeCallback, WrapPersistent(this)))); |
| |
| GetHandle().PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, |
| callback->UnboundCallback(), |
| callback->AsUserdata()); |
| |
| // WebGPU guarantees that promises are resolved in finite time so we |
| // need to ensure commands are flushed. |
| EnsureFlush(ToEventLoop(script_state)); |
| return promise; |
| } |
| |
| void GPUDevice::OnPopErrorScopeCallback( |
| ScriptPromiseResolver<IDLNullable<GPUError>>* resolver, |
| wgpu::PopErrorScopeStatus status, |
| wgpu::ErrorType type, |
| wgpu::StringView message) { |
| switch (status) { |
| case wgpu::PopErrorScopeStatus::CallbackCancelled: |
| resolver->RejectWithDOMException(DOMExceptionCode::kOperationError, |
| "Instance dropped in popErrorScope"); |
| return; |
| case wgpu::PopErrorScopeStatus::Success: |
| break; |
| case wgpu::PopErrorScopeStatus::Error: |
| resolver->RejectWithDOMException(DOMExceptionCode::kOperationError, |
| StringFromASCIIAndUTF8(message)); |
| return; |
| } |
| switch (type) { |
| case wgpu::ErrorType::NoError: |
| resolver->Resolve(nullptr); |
| break; |
| case wgpu::ErrorType::OutOfMemory: |
| resolver->Resolve(MakeGarbageCollected<GPUOutOfMemoryError>( |
| StringFromASCIIAndUTF8(message))); |
| break; |
| case wgpu::ErrorType::Validation: |
| resolver->Resolve(MakeGarbageCollected<GPUValidationError>( |
| StringFromASCIIAndUTF8(message))); |
| break; |
| case wgpu::ErrorType::Internal: |
| resolver->Resolve(MakeGarbageCollected<GPUInternalError>( |
| StringFromASCIIAndUTF8(message))); |
| break; |
| case wgpu::ErrorType::Unknown: |
| resolver->RejectWithDOMException(DOMExceptionCode::kOperationError, |
| "Unknown failure in popErrorScope"); |
| break; |
| } |
| } |
| |
| ExecutionContext* GPUDevice::GetExecutionContext() const { |
| return ExecutionContextClient::GetExecutionContext(); |
| } |
| |
| const AtomicString& GPUDevice::InterfaceName() const { |
| return event_target_names::kGPUDevice; |
| } |
| |
| void GPUDevice::Trace(Visitor* visitor) const { |
| visitor->Trace(adapter_); |
| visitor->Trace(features_); |
| visitor->Trace(limits_); |
| visitor->Trace(adapter_info_); |
| visitor->Trace(queue_); |
| visitor->Trace(lost_property_); |
| visitor->Trace(external_texture_cache_); |
| visitor->Trace(textures_with_mailbox_); |
| visitor->Trace(buffers_with_mailbox_); |
| visitor->Trace(mappable_buffers_); |
| ExecutionContextClient::Trace(visitor); |
| EventTarget::Trace(visitor); |
| } |
| |
| void GPUDevice::Dispose() { |
| // This call accesses other GC objects, so it cannot be called inside GC |
| // objects destructors. Instead call it in the pre-finalizer. |
| if (external_texture_cache_ != nullptr) { |
| external_texture_cache_->Destroy(); |
| } |
| } |
| |
| void GPUDevice::DissociateMailboxes() { |
| for (auto& texture : textures_with_mailbox_) { |
| texture->DissociateMailbox(); |
| } |
| textures_with_mailbox_.clear(); |
| |
| for (auto& buffer : buffers_with_mailbox_) { |
| buffer->DissociateMailbox(); |
| } |
| buffers_with_mailbox_.clear(); |
| } |
| |
| void GPUDevice::UnmapAllMappableBuffers(v8::Isolate* isolate) { |
| for (GPUBuffer* buffer : mappable_buffers_) { |
| buffer->unmap(isolate); |
| } |
| } |
| |
| void GPUDevice::TrackMappableBuffer(GPUBuffer* buffer) { |
| mappable_buffers_.insert(buffer); |
| } |
| |
| void GPUDevice::UntrackMappableBuffer(GPUBuffer* buffer) { |
| mappable_buffers_.erase(buffer); |
| } |
| |
| void GPUDevice::TrackTextureWithMailbox(GPUTexture* texture) { |
| DCHECK(texture); |
| textures_with_mailbox_.insert(texture); |
| } |
| |
| void GPUDevice::UntrackTextureWithMailbox(GPUTexture* texture) { |
| DCHECK(texture); |
| textures_with_mailbox_.erase(texture); |
| } |
| |
| void GPUDevice::TrackBufferWithMailbox(GPUBuffer* buffer) { |
| DCHECK(buffer); |
| buffers_with_mailbox_.insert(buffer); |
| } |
| |
| void GPUDevice::UntrackBufferWithMailbox(GPUBuffer* buffer) { |
| DCHECK(buffer); |
| buffers_with_mailbox_.erase(buffer); |
| } |
| |
| void GPUDevice::SetDescriptorCallbacks(wgpu::DeviceDescriptor& dawn_desc) { |
| // Set the uncaptured error callback first because it's ownership will be |
| // passed to the device lost callback immediately after. |
| std::unique_ptr<WGPURepeatingCallback<wgpu::UncapturedErrorCallback<void>>> |
| error_callback(BindWGPURepeatingCallback(&GPUDevice::OnUncapturedError, |
| WrapWeakPersistent(this))); |
| dawn_desc.SetUncapturedErrorCallback(error_callback->UnboundCallback(), |
| error_callback->AsUserdata()); |
| |
| auto* lost_callback = MakeWGPUOnceCallback( |
| BindOnce(&GPUDevice::OnDeviceLost, WrapWeakPersistent(this), |
| std::move(error_callback))); |
| dawn_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, |
| lost_callback->UnboundCallback(), |
| lost_callback->AsUserdata()); |
| } |
| } // namespace blink |