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