tor-browser

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

XRWebGLLayer.cpp (9169B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/XRWebGLLayer.h"
      8 
      9 #include "ClientWebGLContext.h"
     10 #include "GLContext.h"
     11 #include "MozFramebuffer.h"
     12 #include "ScopedGLHelpers.h"
     13 #include "VRDisplayClient.h"
     14 #include "WebGLFramebuffer.h"
     15 #include "mozilla/StaticPrefs_dom.h"
     16 #include "mozilla/StaticPrefs_webgl.h"
     17 #include "mozilla/dom/WebGLRenderingContextBinding.h"
     18 #include "mozilla/dom/XRSession.h"
     19 #include "mozilla/dom/XRView.h"
     20 #include "mozilla/dom/XRViewport.h"
     21 #include "nsContentUtils.h"
     22 #include "nsIScriptError.h"
     23 
     24 using namespace mozilla::gl;
     25 
     26 namespace mozilla::dom {
     27 
     28 static constexpr float XR_FRAMEBUFFER_MIN_SCALE = 0.2f;
     29 
     30 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRWebGLLayer, mParent, mSession, mWebGL,
     31                                      mFramebuffer, mLeftViewport,
     32                                      mRightViewport)
     33 
     34 XRWebGLLayer::XRWebGLLayer(
     35    nsISupports* aParent, XRSession& aSession, bool aIgnoreDepthValues,
     36    double aFramebufferScaleFactor,
     37    RefPtr<mozilla::ClientWebGLContext> aWebGLContext,
     38    RefPtr<WebGLFramebufferJS> aFramebuffer,
     39    const Maybe<const webgl::OpaqueFramebufferOptions>& aOptions)
     40    : mParent(aParent),
     41      mSession(&aSession),
     42      mWebGL(std::move(aWebGLContext)),
     43      mFramebufferScaleFactor(aFramebufferScaleFactor),
     44      mCompositionDisabled(!aSession.IsImmersive()),
     45      mIgnoreDepthValues(aIgnoreDepthValues),
     46      mFramebuffer(std::move(aFramebuffer)),
     47      mFramebufferOptions(aOptions) {}
     48 
     49 XRWebGLLayer::~XRWebGLLayer() { DeleteFramebuffer(); }
     50 
     51 void XRWebGLLayer::DeleteFramebuffer() {
     52  if (mFramebuffer) {
     53    mWebGL->DeleteFramebuffer(mFramebuffer.get(), true);
     54    mFramebuffer = nullptr;
     55  }
     56 }
     57 
     58 /* static */
     59 already_AddRefed<XRWebGLLayer> XRWebGLLayer::Constructor(
     60    const GlobalObject& aGlobal, XRSession& aSession,
     61    const WebGLRenderingContextOrWebGL2RenderingContext& aXRWebGLContext,
     62    const XRWebGLLayerInit& aXRWebGLLayerInitDict, ErrorResult& aRv) {
     63  // https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
     64 
     65  // Depth not supported in XR Compositor yet.
     66  const bool ignoreDepthValues = true;
     67 
     68  // If session’s ended value is true, throw an InvalidStateError and abort
     69  // these steps.
     70  if (aSession.IsEnded()) {
     71    aRv.ThrowInvalidStateError(
     72        "Can not create an XRWebGLLayer with an XRSession that has ended.");
     73    return nullptr;
     74  }
     75  gfx::VRDisplayClient* display = aSession.GetDisplayClient();
     76  const gfx::VRDisplayInfo& displayInfo = display->GetDisplayInfo();
     77  const gfx::VRDisplayState& displayState = displayInfo.mDisplayState;
     78 
     79  RefPtr<ClientWebGLContext> gl;
     80  if (aXRWebGLContext.IsWebGLRenderingContext()) {
     81    gl = &aXRWebGLContext.GetAsWebGLRenderingContext();
     82  } else {
     83    gl = &aXRWebGLContext.GetAsWebGL2RenderingContext();
     84  }
     85 
     86  // If context is lost, throw an InvalidStateError and abort these steps.
     87  if (gl->IsContextLost()) {
     88    aRv.ThrowInvalidStateError(
     89        "Could not create an XRWebGLLayer, as the WebGL context was lost.");
     90    return nullptr;
     91  }
     92 
     93  RefPtr<mozilla::WebGLFramebufferJS> framebuffer;
     94  Maybe<const webgl::OpaqueFramebufferOptions> framebufferOptions;
     95  if (aSession.IsImmersive()) {
     96    // If session is an immersive session and context’s XR compatible boolean
     97    // is false, throw an InvalidStateError and abort these steps.
     98    if (!gl->IsXRCompatible()) {
     99      aRv.ThrowInvalidStateError(
    100          "Can not create an XRWebGLLayer without first calling "
    101          "makeXRCompatible "
    102          "on the WebGLRenderingContext or WebGL2RenderingContext.");
    103      return nullptr;
    104    }
    105 
    106    const auto document = gl->GetCanvas()->OwnerDoc();
    107    if (aXRWebGLLayerInitDict.mAlpha) {
    108      nsContentUtils::ReportToConsoleNonLocalized(
    109          u"XRWebGLLayer doesn't support no alpha value. "
    110          "Alpha will be enabled."_ns,
    111          nsIScriptError::warningFlag, "DOM"_ns, document);
    112    }
    113    if (aXRWebGLLayerInitDict.mDepth != aXRWebGLLayerInitDict.mStencil) {
    114      nsContentUtils::ReportToConsoleNonLocalized(
    115          nsLiteralString(
    116              u"XRWebGLLayer doesn't support separate "
    117              "depth or stencil buffers. They will be enabled together."),
    118          nsIScriptError::warningFlag, "DOM"_ns, document);
    119    }
    120 
    121    bool antialias = aXRWebGLLayerInitDict.mAntialias;
    122    if (antialias && !StaticPrefs::webgl_msaa_force()) {
    123      antialias = false;
    124      nsContentUtils::ReportToConsoleNonLocalized(
    125          u"XRWebGLLayer antialiasing is not supported."
    126          "Antialiasing will be disabled."_ns,
    127          nsIScriptError::warningFlag, "DOM"_ns, document);
    128    }
    129 
    130    webgl::OpaqueFramebufferOptions options;
    131    options.antialias = antialias;
    132    options.depthStencil =
    133        aXRWebGLLayerInitDict.mDepth || aXRWebGLLayerInitDict.mStencil;
    134 
    135    // Clamp the requested framebuffer size to ensure it's not too
    136    // small to see or larger than the max native resolution.
    137    const float maxScale =
    138        std::max(displayState.nativeFramebufferScaleFactor, 1.0f);
    139    const float scaleFactor =
    140        std::max(XR_FRAMEBUFFER_MIN_SCALE,
    141                 std::min((float)aXRWebGLLayerInitDict.mFramebufferScaleFactor,
    142                          maxScale));
    143 
    144    options.width =
    145        (int32_t)ceilf(2.0f * displayState.eyeResolution.width * scaleFactor);
    146    options.height =
    147        (int32_t)ceilf(displayState.eyeResolution.height * scaleFactor);
    148    framebuffer = gl->CreateOpaqueFramebuffer(options);
    149 
    150    if (!framebuffer) {
    151      aRv.ThrowOperationError(
    152          "Could not create an XRWebGLLayer. XRFramebuffer creation failed.");
    153      return nullptr;
    154    }
    155    framebufferOptions.emplace(options);
    156  }
    157 
    158  RefPtr<XRWebGLLayer> obj =
    159      new XRWebGLLayer(aGlobal.GetAsSupports(), aSession, ignoreDepthValues,
    160                       aXRWebGLLayerInitDict.mFramebufferScaleFactor, gl,
    161                       framebuffer, framebufferOptions);
    162  return obj.forget();
    163 }
    164 
    165 JSObject* XRWebGLLayer::WrapObject(JSContext* aCx,
    166                                   JS::Handle<JSObject*> aGivenProto) {
    167  return XRWebGLLayer_Binding::Wrap(aCx, this, aGivenProto);
    168 }
    169 
    170 nsISupports* XRWebGLLayer::GetParentObject() const { return mParent; }
    171 
    172 bool XRWebGLLayer::Antialias() {
    173  if (mFramebufferOptions) {
    174    return mFramebufferOptions->antialias;
    175  }
    176  return mWebGL->ActualContextParameters().antialias;
    177 }
    178 
    179 bool XRWebGLLayer::Depth() {
    180  if (mFramebufferOptions) {
    181    return mFramebufferOptions->depthStencil;
    182  }
    183  return mWebGL->ActualContextParameters().depth;
    184 }
    185 
    186 bool XRWebGLLayer::Stencil() {
    187  if (mFramebufferOptions) {
    188    return mFramebufferOptions->depthStencil;
    189  }
    190  return mWebGL->ActualContextParameters().stencil;
    191 }
    192 
    193 bool XRWebGLLayer::Alpha() {
    194  if (mFramebufferOptions) {
    195    // Alpha is always true when using Opaque Framebuffers.
    196    return true;
    197  }
    198  return mWebGL->ActualContextParameters().alpha;
    199 }
    200 
    201 bool XRWebGLLayer::IgnoreDepthValues() { return mIgnoreDepthValues; }
    202 
    203 WebGLFramebufferJS* XRWebGLLayer::GetFramebuffer() {
    204  return mFramebuffer.get();
    205 }
    206 
    207 uint32_t XRWebGLLayer::FramebufferWidth() {
    208  if (mFramebufferOptions) {
    209    return mFramebufferOptions->width;
    210  }
    211  return mWebGL->GetWidth();
    212 }
    213 
    214 uint32_t XRWebGLLayer::FramebufferHeight() {
    215  if (mFramebufferOptions) {
    216    return mFramebufferOptions->height;
    217  }
    218  return mWebGL->GetHeight();
    219 }
    220 
    221 already_AddRefed<XRViewport> XRWebGLLayer::GetViewport(const XRView& aView) {
    222  const int32_t width = (aView.Eye() == XREye::None) ? FramebufferWidth()
    223                                                     : (FramebufferWidth() / 2);
    224  gfx::IntRect rect(0, 0, width, FramebufferHeight());
    225  if (aView.Eye() == XREye::Right) {
    226    rect.x = width;
    227  }
    228  RefPtr<XRViewport>& viewport =
    229      aView.Eye() == XREye::Right ? mRightViewport : mLeftViewport;
    230  if (!viewport) {
    231    viewport = new XRViewport(mParent, rect);
    232  } else {
    233    viewport->mRect = rect;
    234  }
    235  RefPtr<XRViewport> result = viewport;
    236  return result.forget();
    237 }
    238 
    239 // https://www.w3.org/TR/webxr/#dom-xrwebgllayer-getnativeframebufferscalefactor
    240 /* static */ double XRWebGLLayer::GetNativeFramebufferScaleFactor(
    241    const GlobalObject& aGlobal, const XRSession& aSession) {
    242  if (aSession.IsEnded()) {
    243    return 0.0f;
    244  }
    245  if (!aSession.IsImmersive()) {
    246    return 1.0f;
    247  }
    248 
    249  const gfx::VRDisplayInfo& displayInfo =
    250      aSession.GetDisplayClient()->GetDisplayInfo();
    251  return displayInfo.mDisplayState.nativeFramebufferScaleFactor;
    252 }
    253 
    254 void XRWebGLLayer::StartAnimationFrame() {
    255  if (mFramebuffer) {
    256    mWebGL->SetFramebufferIsInOpaqueRAF(mFramebuffer.get(), true);
    257  }
    258 }
    259 
    260 void XRWebGLLayer::EndAnimationFrame() {
    261  if (mFramebuffer) {
    262    mWebGL->SetFramebufferIsInOpaqueRAF(mFramebuffer.get(), false);
    263  }
    264 }
    265 
    266 HTMLCanvasElement* XRWebGLLayer::GetCanvas() { return mWebGL->GetCanvas(); }
    267 
    268 void XRWebGLLayer::SessionEnded() { DeleteFramebuffer(); }
    269 
    270 }  // namespace mozilla::dom