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