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/. */
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 "mozilla/HoldDropJSObjects.h"
13 #include "WebGLFramebuffer.h"
14 #include "mozilla/StaticPrefs_dom.h"
15 #include "mozilla/StaticPrefs_webgl.h"
16 #include "GLContext.h"
17 #include "ScopedGLHelpers.h"
18 #include "MozFramebuffer.h"
19 #include "VRDisplayClient.h"
20 #include "ClientWebGLContext.h"
21 #include "nsContentUtils.h"
22 #include "nsIScriptError.h"
24 using namespace mozilla::gl
;
26 namespace mozilla::dom
{
28 static constexpr float XR_FRAMEBUFFER_MIN_SCALE
= 0.2f
;
30 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRWebGLLayer
, mParent
, mSession
, mWebGL
,
31 mFramebuffer
, mLeftViewport
,
33 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(XRWebGLLayer
, AddRef
)
34 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(XRWebGLLayer
, Release
)
36 XRWebGLLayer::XRWebGLLayer(
37 nsISupports
* aParent
, XRSession
& aSession
, bool aIgnoreDepthValues
,
38 double aFramebufferScaleFactor
,
39 RefPtr
<mozilla::ClientWebGLContext
> aWebGLContext
,
40 RefPtr
<WebGLFramebufferJS
> aFramebuffer
,
41 const Maybe
<const webgl::OpaqueFramebufferOptions
>& aOptions
)
44 mWebGL(std::move(aWebGLContext
)),
45 mFramebufferScaleFactor(aFramebufferScaleFactor
),
46 mCompositionDisabled(!aSession
.IsImmersive()),
47 mIgnoreDepthValues(aIgnoreDepthValues
),
48 mFramebuffer(std::move(aFramebuffer
)),
49 mFramebufferOptions(aOptions
) {
50 mozilla::HoldJSObjects(this);
53 XRWebGLLayer::~XRWebGLLayer() {
55 mozilla::DropJSObjects(this);
58 void XRWebGLLayer::DeleteFramebuffer() {
60 mWebGL
->DeleteFramebuffer(mFramebuffer
.get(), true);
61 mFramebuffer
= nullptr;
66 already_AddRefed
<XRWebGLLayer
> XRWebGLLayer::Constructor(
67 const GlobalObject
& aGlobal
, XRSession
& aSession
,
68 const WebGLRenderingContextOrWebGL2RenderingContext
& aXRWebGLContext
,
69 const XRWebGLLayerInit
& aXRWebGLLayerInitDict
, ErrorResult
& aRv
) {
70 // https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
72 // Depth not supported in XR Compositor yet.
73 const bool ignoreDepthValues
= true;
75 // If session’s ended value is true, throw an InvalidStateError and abort
77 if (aSession
.IsEnded()) {
78 aRv
.ThrowInvalidStateError(
79 "Can not create an XRWebGLLayer with an XRSession that has ended.");
82 gfx::VRDisplayClient
* display
= aSession
.GetDisplayClient();
83 const gfx::VRDisplayInfo
& displayInfo
= display
->GetDisplayInfo();
84 const gfx::VRDisplayState
& displayState
= displayInfo
.mDisplayState
;
86 RefPtr
<ClientWebGLContext
> gl
;
87 if (aXRWebGLContext
.IsWebGLRenderingContext()) {
88 gl
= &aXRWebGLContext
.GetAsWebGLRenderingContext();
90 gl
= &aXRWebGLContext
.GetAsWebGL2RenderingContext();
93 // If context is lost, throw an InvalidStateError and abort these steps.
94 if (gl
->IsContextLost()) {
95 aRv
.ThrowInvalidStateError(
96 "Could not create an XRWebGLLayer, as the WebGL context was lost.");
100 RefPtr
<mozilla::WebGLFramebufferJS
> framebuffer
;
101 Maybe
<const webgl::OpaqueFramebufferOptions
> framebufferOptions
;
102 if (aSession
.IsImmersive()) {
103 // If session is an immersive session and context’s XR compatible boolean
104 // is false, throw an InvalidStateError and abort these steps.
105 if (!gl
->IsXRCompatible()) {
106 aRv
.ThrowInvalidStateError(
107 "Can not create an XRWebGLLayer without first calling "
109 "on the WebGLRenderingContext or WebGL2RenderingContext.");
113 const auto document
= gl
->GetParentObject()->OwnerDoc();
114 if (aXRWebGLLayerInitDict
.mAlpha
) {
115 nsContentUtils::ReportToConsoleNonLocalized(
116 u
"XRWebGLLayer doesn't support no alpha value. "
117 "Alpha will be enabled."_ns
,
118 nsIScriptError::warningFlag
, "DOM"_ns
, document
);
120 if (aXRWebGLLayerInitDict
.mDepth
!= aXRWebGLLayerInitDict
.mStencil
) {
121 nsContentUtils::ReportToConsoleNonLocalized(
123 u
"XRWebGLLayer doesn't support separate "
124 "depth or stencil buffers. They will be enabled together."),
125 nsIScriptError::warningFlag
, "DOM"_ns
, document
);
128 bool antialias
= aXRWebGLLayerInitDict
.mAntialias
;
129 if (antialias
&& !StaticPrefs::webgl_msaa_force()) {
131 nsContentUtils::ReportToConsoleNonLocalized(
132 u
"XRWebGLLayer antialiasing is not supported."
133 "Antialiasing will be disabled."_ns
,
134 nsIScriptError::warningFlag
, "DOM"_ns
, document
);
137 webgl::OpaqueFramebufferOptions options
;
138 options
.antialias
= antialias
;
139 options
.depthStencil
=
140 aXRWebGLLayerInitDict
.mDepth
|| aXRWebGLLayerInitDict
.mStencil
;
142 // Clamp the requested framebuffer size to ensure it's not too
143 // small to see or larger than the max native resolution.
144 const float maxScale
=
145 std::max(displayState
.nativeFramebufferScaleFactor
, 1.0f
);
146 const float scaleFactor
=
147 std::max(XR_FRAMEBUFFER_MIN_SCALE
,
148 std::min((float)aXRWebGLLayerInitDict
.mFramebufferScaleFactor
,
152 (int32_t)ceilf(2.0f
* displayState
.eyeResolution
.width
* scaleFactor
);
154 (int32_t)ceilf(displayState
.eyeResolution
.height
* scaleFactor
);
155 framebuffer
= gl
->CreateOpaqueFramebuffer(options
);
158 aRv
.ThrowOperationError(
159 "Could not create an XRWebGLLayer. XRFramebuffer creation failed.");
162 framebufferOptions
.emplace(options
);
165 RefPtr
<XRWebGLLayer
> obj
=
166 new XRWebGLLayer(aGlobal
.GetAsSupports(), aSession
, ignoreDepthValues
,
167 aXRWebGLLayerInitDict
.mFramebufferScaleFactor
, gl
,
168 framebuffer
, framebufferOptions
);
172 JSObject
* XRWebGLLayer::WrapObject(JSContext
* aCx
,
173 JS::Handle
<JSObject
*> aGivenProto
) {
174 return XRWebGLLayer_Binding::Wrap(aCx
, this, aGivenProto
);
177 nsISupports
* XRWebGLLayer::GetParentObject() const { return mParent
; }
179 bool XRWebGLLayer::Antialias() {
180 if (mFramebufferOptions
) {
181 return mFramebufferOptions
->antialias
;
183 return mWebGL
->ActualContextParameters().antialias
;
186 bool XRWebGLLayer::Depth() {
187 if (mFramebufferOptions
) {
188 return mFramebufferOptions
->depthStencil
;
190 return mWebGL
->ActualContextParameters().depth
;
193 bool XRWebGLLayer::Stencil() {
194 if (mFramebufferOptions
) {
195 return mFramebufferOptions
->depthStencil
;
197 return mWebGL
->ActualContextParameters().stencil
;
200 bool XRWebGLLayer::Alpha() {
201 if (mFramebufferOptions
) {
202 // Alpha is always true when using Opaque Framebuffers.
205 return mWebGL
->ActualContextParameters().alpha
;
208 bool XRWebGLLayer::IgnoreDepthValues() { return mIgnoreDepthValues
; }
210 WebGLFramebufferJS
* XRWebGLLayer::GetFramebuffer() {
211 return mFramebuffer
.get();
214 uint32_t XRWebGLLayer::FramebufferWidth() {
215 if (mFramebufferOptions
) {
216 return mFramebufferOptions
->width
;
218 return mWebGL
->GetWidth();
221 uint32_t XRWebGLLayer::FramebufferHeight() {
222 if (mFramebufferOptions
) {
223 return mFramebufferOptions
->height
;
225 return mWebGL
->GetHeight();
228 already_AddRefed
<XRViewport
> XRWebGLLayer::GetViewport(const XRView
& aView
) {
229 const int32_t width
= (aView
.Eye() == XREye::None
) ? FramebufferWidth()
230 : (FramebufferWidth() / 2);
231 gfx::IntRect
rect(0, 0, width
, FramebufferHeight());
232 if (aView
.Eye() == XREye::Right
) {
235 RefPtr
<XRViewport
>& viewport
=
236 aView
.Eye() == XREye::Right
? mRightViewport
: mLeftViewport
;
238 viewport
= new XRViewport(mParent
, rect
);
240 viewport
->mRect
= rect
;
242 RefPtr
<XRViewport
> result
= viewport
;
243 return result
.forget();
246 // https://www.w3.org/TR/webxr/#dom-xrwebgllayer-getnativeframebufferscalefactor
247 /* static */ double XRWebGLLayer::GetNativeFramebufferScaleFactor(
248 const GlobalObject
& aGlobal
, const XRSession
& aSession
) {
249 if (aSession
.IsEnded()) {
252 if (!aSession
.IsImmersive()) {
256 const gfx::VRDisplayInfo
& displayInfo
=
257 aSession
.GetDisplayClient()->GetDisplayInfo();
258 return displayInfo
.mDisplayState
.nativeFramebufferScaleFactor
;
261 void XRWebGLLayer::StartAnimationFrame() {
263 mWebGL
->SetFramebufferIsInOpaqueRAF(mFramebuffer
.get(), true);
267 void XRWebGLLayer::EndAnimationFrame() {
269 mWebGL
->SetFramebufferIsInOpaqueRAF(mFramebuffer
.get(), false);
273 HTMLCanvasElement
* XRWebGLLayer::GetCanvas() {
274 return mWebGL
->GetParentObject();
277 void XRWebGLLayer::SessionEnded() { DeleteFramebuffer(); }
279 } // namespace mozilla::dom