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 "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 "nsContentUtils.h"
21 #include "nsIScriptError.h"
23 using namespace mozilla::gl
;
25 namespace mozilla::dom
{
27 static constexpr float XR_FRAMEBUFFER_MIN_SCALE
= 0.2f
;
29 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRWebGLLayer
, mParent
, mSession
, mWebGL
,
30 mFramebuffer
, mLeftViewport
,
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
)
41 mWebGL(std::move(aWebGLContext
)),
42 mFramebufferScaleFactor(aFramebufferScaleFactor
),
43 mCompositionDisabled(!aSession
.IsImmersive()),
44 mIgnoreDepthValues(aIgnoreDepthValues
),
45 mFramebuffer(std::move(aFramebuffer
)),
46 mFramebufferOptions(aOptions
) {}
48 XRWebGLLayer::~XRWebGLLayer() { DeleteFramebuffer(); }
50 void XRWebGLLayer::DeleteFramebuffer() {
52 mWebGL
->DeleteFramebuffer(mFramebuffer
.get(), true);
53 mFramebuffer
= nullptr;
58 already_AddRefed
<XRWebGLLayer
> XRWebGLLayer::Constructor(
59 const GlobalObject
& aGlobal
, XRSession
& aSession
,
60 const WebGLRenderingContextOrWebGL2RenderingContext
& aXRWebGLContext
,
61 const XRWebGLLayerInit
& aXRWebGLLayerInitDict
, ErrorResult
& aRv
) {
62 // https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
64 // Depth not supported in XR Compositor yet.
65 const bool ignoreDepthValues
= true;
67 // If session’s ended value is true, throw an InvalidStateError and abort
69 if (aSession
.IsEnded()) {
70 aRv
.ThrowInvalidStateError(
71 "Can not create an XRWebGLLayer with an XRSession that has ended.");
74 gfx::VRDisplayClient
* display
= aSession
.GetDisplayClient();
75 const gfx::VRDisplayInfo
& displayInfo
= display
->GetDisplayInfo();
76 const gfx::VRDisplayState
& displayState
= displayInfo
.mDisplayState
;
78 RefPtr
<ClientWebGLContext
> gl
;
79 if (aXRWebGLContext
.IsWebGLRenderingContext()) {
80 gl
= &aXRWebGLContext
.GetAsWebGLRenderingContext();
82 gl
= &aXRWebGLContext
.GetAsWebGL2RenderingContext();
85 // If context is lost, throw an InvalidStateError and abort these steps.
86 if (gl
->IsContextLost()) {
87 aRv
.ThrowInvalidStateError(
88 "Could not create an XRWebGLLayer, as the WebGL context was lost.");
92 RefPtr
<mozilla::WebGLFramebufferJS
> framebuffer
;
93 Maybe
<const webgl::OpaqueFramebufferOptions
> framebufferOptions
;
94 if (aSession
.IsImmersive()) {
95 // If session is an immersive session and context’s XR compatible boolean
96 // is false, throw an InvalidStateError and abort these steps.
97 if (!gl
->IsXRCompatible()) {
98 aRv
.ThrowInvalidStateError(
99 "Can not create an XRWebGLLayer without first calling "
101 "on the WebGLRenderingContext or WebGL2RenderingContext.");
105 const auto document
= gl
->GetCanvas()->OwnerDoc();
106 if (aXRWebGLLayerInitDict
.mAlpha
) {
107 nsContentUtils::ReportToConsoleNonLocalized(
108 u
"XRWebGLLayer doesn't support no alpha value. "
109 "Alpha will be enabled."_ns
,
110 nsIScriptError::warningFlag
, "DOM"_ns
, document
);
112 if (aXRWebGLLayerInitDict
.mDepth
!= aXRWebGLLayerInitDict
.mStencil
) {
113 nsContentUtils::ReportToConsoleNonLocalized(
115 u
"XRWebGLLayer doesn't support separate "
116 "depth or stencil buffers. They will be enabled together."),
117 nsIScriptError::warningFlag
, "DOM"_ns
, document
);
120 bool antialias
= aXRWebGLLayerInitDict
.mAntialias
;
121 if (antialias
&& !StaticPrefs::webgl_msaa_force()) {
123 nsContentUtils::ReportToConsoleNonLocalized(
124 u
"XRWebGLLayer antialiasing is not supported."
125 "Antialiasing will be disabled."_ns
,
126 nsIScriptError::warningFlag
, "DOM"_ns
, document
);
129 webgl::OpaqueFramebufferOptions options
;
130 options
.antialias
= antialias
;
131 options
.depthStencil
=
132 aXRWebGLLayerInitDict
.mDepth
|| aXRWebGLLayerInitDict
.mStencil
;
134 // Clamp the requested framebuffer size to ensure it's not too
135 // small to see or larger than the max native resolution.
136 const float maxScale
=
137 std::max(displayState
.nativeFramebufferScaleFactor
, 1.0f
);
138 const float scaleFactor
=
139 std::max(XR_FRAMEBUFFER_MIN_SCALE
,
140 std::min((float)aXRWebGLLayerInitDict
.mFramebufferScaleFactor
,
144 (int32_t)ceilf(2.0f
* displayState
.eyeResolution
.width
* scaleFactor
);
146 (int32_t)ceilf(displayState
.eyeResolution
.height
* scaleFactor
);
147 framebuffer
= gl
->CreateOpaqueFramebuffer(options
);
150 aRv
.ThrowOperationError(
151 "Could not create an XRWebGLLayer. XRFramebuffer creation failed.");
154 framebufferOptions
.emplace(options
);
157 RefPtr
<XRWebGLLayer
> obj
=
158 new XRWebGLLayer(aGlobal
.GetAsSupports(), aSession
, ignoreDepthValues
,
159 aXRWebGLLayerInitDict
.mFramebufferScaleFactor
, gl
,
160 framebuffer
, framebufferOptions
);
164 JSObject
* XRWebGLLayer::WrapObject(JSContext
* aCx
,
165 JS::Handle
<JSObject
*> aGivenProto
) {
166 return XRWebGLLayer_Binding::Wrap(aCx
, this, aGivenProto
);
169 nsISupports
* XRWebGLLayer::GetParentObject() const { return mParent
; }
171 bool XRWebGLLayer::Antialias() {
172 if (mFramebufferOptions
) {
173 return mFramebufferOptions
->antialias
;
175 return mWebGL
->ActualContextParameters().antialias
;
178 bool XRWebGLLayer::Depth() {
179 if (mFramebufferOptions
) {
180 return mFramebufferOptions
->depthStencil
;
182 return mWebGL
->ActualContextParameters().depth
;
185 bool XRWebGLLayer::Stencil() {
186 if (mFramebufferOptions
) {
187 return mFramebufferOptions
->depthStencil
;
189 return mWebGL
->ActualContextParameters().stencil
;
192 bool XRWebGLLayer::Alpha() {
193 if (mFramebufferOptions
) {
194 // Alpha is always true when using Opaque Framebuffers.
197 return mWebGL
->ActualContextParameters().alpha
;
200 bool XRWebGLLayer::IgnoreDepthValues() { return mIgnoreDepthValues
; }
202 WebGLFramebufferJS
* XRWebGLLayer::GetFramebuffer() {
203 return mFramebuffer
.get();
206 uint32_t XRWebGLLayer::FramebufferWidth() {
207 if (mFramebufferOptions
) {
208 return mFramebufferOptions
->width
;
210 return mWebGL
->GetWidth();
213 uint32_t XRWebGLLayer::FramebufferHeight() {
214 if (mFramebufferOptions
) {
215 return mFramebufferOptions
->height
;
217 return mWebGL
->GetHeight();
220 already_AddRefed
<XRViewport
> XRWebGLLayer::GetViewport(const XRView
& aView
) {
221 const int32_t width
= (aView
.Eye() == XREye::None
) ? FramebufferWidth()
222 : (FramebufferWidth() / 2);
223 gfx::IntRect
rect(0, 0, width
, FramebufferHeight());
224 if (aView
.Eye() == XREye::Right
) {
227 RefPtr
<XRViewport
>& viewport
=
228 aView
.Eye() == XREye::Right
? mRightViewport
: mLeftViewport
;
230 viewport
= new XRViewport(mParent
, rect
);
232 viewport
->mRect
= rect
;
234 RefPtr
<XRViewport
> result
= viewport
;
235 return result
.forget();
238 // https://www.w3.org/TR/webxr/#dom-xrwebgllayer-getnativeframebufferscalefactor
239 /* static */ double XRWebGLLayer::GetNativeFramebufferScaleFactor(
240 const GlobalObject
& aGlobal
, const XRSession
& aSession
) {
241 if (aSession
.IsEnded()) {
244 if (!aSession
.IsImmersive()) {
248 const gfx::VRDisplayInfo
& displayInfo
=
249 aSession
.GetDisplayClient()->GetDisplayInfo();
250 return displayInfo
.mDisplayState
.nativeFramebufferScaleFactor
;
253 void XRWebGLLayer::StartAnimationFrame() {
255 mWebGL
->SetFramebufferIsInOpaqueRAF(mFramebuffer
.get(), true);
259 void XRWebGLLayer::EndAnimationFrame() {
261 mWebGL
->SetFramebufferIsInOpaqueRAF(mFramebuffer
.get(), false);
265 HTMLCanvasElement
* XRWebGLLayer::GetCanvas() { return mWebGL
->GetCanvas(); }
267 void XRWebGLLayer::SessionEnded() { DeleteFramebuffer(); }
269 } // namespace mozilla::dom