tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Instance.cpp (7666B)


      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 "Instance.h"
      7 
      8 #include <optional>
      9 #include <string_view>
     10 
     11 #include "Adapter.h"
     12 #include "ipc/WebGPUChild.h"
     13 #include "ipc/WebGPUTypes.h"
     14 #include "js/Value.h"
     15 #include "mozilla/Assertions.h"
     16 #include "mozilla/ErrorResult.h"
     17 #include "mozilla/StaticPrefs_dom.h"
     18 #include "mozilla/dom/Promise.h"
     19 #include "mozilla/dom/WorkerPrivate.h"
     20 #include "mozilla/gfx/CanvasManagerChild.h"
     21 #include "mozilla/gfx/Logging.h"
     22 #include "mozilla/gfx/gfxVars.h"
     23 #include "mozilla/webgpu/ffi/wgpu.h"
     24 #include "nsDebug.h"
     25 #include "nsIGlobalObject.h"
     26 #include "nsString.h"
     27 #include "nsStringFwd.h"
     28 
     29 namespace mozilla::webgpu {
     30 
     31 GPU_IMPL_CYCLE_COLLECTION(WGSLLanguageFeatures, mParent)
     32 
     33 GPU_IMPL_CYCLE_COLLECTION(Instance, mOwner, mWgslLanguageFeatures)
     34 
     35 static inline nsDependentCString ToCString(const std::string_view s) {
     36  return {s.data(), s.length()};
     37 }
     38 
     39 /* static */ bool Instance::PrefEnabled(JSContext* aCx, JSObject* aObj) {
     40  if (!StaticPrefs::dom_webgpu_enabled()) {
     41    return false;
     42  }
     43 
     44  if (NS_IsMainThread()) {
     45    return true;
     46  }
     47 
     48  dom::WorkerPrivate* wp = dom::GetCurrentThreadWorkerPrivate();
     49  if (wp && wp->IsServiceWorker()) {
     50    return StaticPrefs::dom_webgpu_service_workers_enabled();
     51  }
     52 
     53  return true;
     54 }
     55 
     56 /* static */ bool Instance::ExternalTexturePrefEnabled(JSContext* aCx,
     57                                                       JSObject* aObj) {
     58  return StaticPrefs::dom_webgpu_external_texture_enabled_AtStartup();
     59 }
     60 
     61 /*static*/
     62 already_AddRefed<Instance> Instance::Create(nsIGlobalObject* aOwner) {
     63  RefPtr<Instance> result = new Instance(aOwner);
     64  return result.forget();
     65 }
     66 
     67 Instance::Instance(nsIGlobalObject* aOwner)
     68    : mOwner(aOwner), mWgslLanguageFeatures(new WGSLLanguageFeatures(this)) {
     69  // Populate `mWgslLanguageFeatures`.
     70  IgnoredErrorResult rv;
     71  nsCString wgslFeature;
     72  for (size_t i = 0;; ++i) {
     73    wgslFeature.Truncate(0);
     74    ffi::wgpu_client_instance_get_wgsl_language_feature(&wgslFeature, i);
     75    if (wgslFeature.IsEmpty()) {
     76      break;
     77    }
     78    NS_ConvertASCIItoUTF16 feature{wgslFeature};
     79    this->mWgslLanguageFeatures->Add(feature, rv);
     80    if (rv.Failed()) {
     81      if (rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)) {
     82        // This is fine; something went wrong with the JS scope we're in, and we
     83        // can just let that happen.
     84        NS_WARNING(
     85            "`Instance::Instance`: failed to append WGSL language feature: got "
     86            "`NS_ERROR_UNEXPECTED`");
     87      } else {
     88        MOZ_CRASH_UNSAFE_PRINTF(
     89            "`Instance::Instance`: failed to append WGSL language feature: %d",
     90            rv.ErrorCodeAsInt());
     91      }
     92    }
     93  }
     94 }
     95 
     96 JSObject* Instance::WrapObject(JSContext* cx,
     97                               JS::Handle<JSObject*> givenProto) {
     98  return dom::GPU_Binding::Wrap(cx, this, givenProto);
     99 }
    100 
    101 already_AddRefed<dom::Promise> Instance::RequestAdapter(
    102    const dom::GPURequestAdapterOptions& aOptions, ErrorResult& aRv) {
    103  RefPtr<dom::Promise> promise = dom::Promise::Create(mOwner, aRv);
    104  if (NS_WARN_IF(aRv.Failed())) {
    105    return nullptr;
    106  }
    107 
    108  if (NS_IsMainThread()) {
    109    JSObject* obj = mOwner->GetGlobalJSObject();
    110    if (obj) {
    111      dom::SetUseCounter(obj, eUseCounter_custom_WebgpuRequestAdapter);
    112    }
    113  } else {
    114    dom::SetUseCounter(UseCounterWorker::Custom_WebgpuRequestAdapter);
    115  }
    116 
    117  // -
    118  // Check if we should allow the request.
    119 
    120  std::optional<std::string_view> rejectionMessage = {};
    121  const auto rejectIf = [&rejectionMessage](bool condition,
    122                                            const char* message) {
    123    if (condition && !rejectionMessage.has_value()) {
    124      rejectionMessage = message;
    125    }
    126  };
    127 
    128  rejectIf(!gfx::gfxVars::AllowWebGPU(), "WebGPU is disabled by blocklist.");
    129  rejectIf(!StaticPrefs::dom_webgpu_enabled(),
    130           "WebGPU is disabled because the `dom.webgpu.enabled` pref. is set "
    131           "to `false`.");
    132 #ifdef WIN32
    133 #  ifndef MOZ_DXCOMPILER
    134  rejectIf(true,
    135           "WebGPU is disabled because dxcompiler is unavailable with this "
    136           "build configuration");
    137 #  endif
    138 #endif
    139 
    140  // Check if WebGPU is blocked for this global's domain.
    141  {
    142    const auto prefLock = mozilla::StaticPrefs::dom_webgpu_blocked_domains();
    143    rejectIf(nsContentUtils::IsURIInList(mOwner->GetBaseURI(), *prefLock),
    144             "WebGPU is blocked for this domain by the "
    145             "`dom.webgpu.blocked-domains` pref.");
    146  }
    147 
    148  if (rejectionMessage) {
    149    promise->MaybeRejectWithNotSupportedError(ToCString(*rejectionMessage));
    150    return promise.forget();
    151  }
    152 
    153  // -
    154  // Make the request.
    155 
    156  auto* const canvasManager = gfx::CanvasManagerChild::Get();
    157  if (!canvasManager) {
    158    promise->MaybeRejectWithInvalidStateError(
    159        "Failed to create CanvasManagerChild");
    160    return promise.forget();
    161  }
    162 
    163  RefPtr<WebGPUChild> child = canvasManager->GetWebGPUChild();
    164  if (!child) {
    165    promise->MaybeRejectWithInvalidStateError("Failed to create WebGPUChild");
    166    return promise.forget();
    167  }
    168 
    169  if (aOptions.mFeatureLevel.EqualsASCII("core")) {
    170    // Good! That's all we support.
    171  } else if (aOptions.mFeatureLevel.EqualsASCII("compatibility")) {
    172    dom::AutoJSAPI api;
    173    if (api.Init(mOwner)) {
    174      JS::WarnUTF8(api.cx(),
    175                   "User requested a WebGPU adapter with `featureLevel: "
    176                   "\"compatibility\"`, which is not yet supported; returning "
    177                   "a \"core\"-defaulting adapter for now. Subscribe to "
    178                   "<https://bugzilla.mozilla.org/show_bug.cgi?id=1905951>"
    179                   " for updates on its development in Firefox.");
    180    }
    181  } else {
    182    NS_ConvertUTF16toUTF8 featureLevel(aOptions.mFeatureLevel);
    183    dom::AutoJSAPI api;
    184    if (api.Init(mOwner)) {
    185      JS::WarnUTF8(api.cx(),
    186                   "expected one of `\"core\"` or `\"compatibility\"` for "
    187                   "`GPUAdapter.featureLevel`, got %s",
    188                   featureLevel.get());
    189    }
    190    promise->MaybeResolve(JS::NullValue());
    191    return promise.forget();
    192  }
    193 
    194  if (aOptions.mXrCompatible) {
    195    dom::AutoJSAPI api;
    196    if (api.Init(mOwner)) {
    197      JS::WarnUTF8(
    198          api.cx(),
    199          "User requested a WebGPU adapter with `xrCompatible: true`, "
    200          "but WebXR sessions are not yet supported in WebGPU. Returning "
    201          "a regular adapter for now. Subscribe to "
    202          "<https://bugzilla.mozilla.org/show_bug.cgi?id=1963829>"
    203          " for updates on its development in Firefox.");
    204    }
    205  }
    206 
    207  ffi::WGPUPowerPreference power_preference;
    208  if (aOptions.mPowerPreference.WasPassed()) {
    209    switch (aOptions.mPowerPreference.Value()) {
    210      case dom::GPUPowerPreference::Low_power:
    211        power_preference = ffi::WGPUPowerPreference_LowPower;
    212        break;
    213      case dom::GPUPowerPreference::High_performance:
    214        power_preference = ffi::WGPUPowerPreference_HighPerformance;
    215        break;
    216      default:
    217        MOZ_CRASH("Unexpected `dom::GPUPowerPreference`");
    218    }
    219  } else {
    220    power_preference = ffi::WGPUPowerPreference_None;
    221  }
    222 
    223  RawId adapter_id = ffi::wgpu_client_request_adapter(
    224      child->GetClient(), power_preference, aOptions.mForceFallbackAdapter);
    225 
    226  auto pending_promise = WebGPUChild::PendingRequestAdapterPromise{
    227      RefPtr(promise), RefPtr(this), adapter_id};
    228  child->mPendingRequestAdapterPromises.push_back(std::move(pending_promise));
    229 
    230  return promise.forget();
    231 }
    232 
    233 }  // namespace mozilla::webgpu