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