Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / canvas / XRWebGLLayer.cpp
blob95d7a2cd96b1dc0944671f9ad3735f95272b50f4
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,
31 mRightViewport)
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) {}
48 XRWebGLLayer::~XRWebGLLayer() { DeleteFramebuffer(); }
50 void XRWebGLLayer::DeleteFramebuffer() {
51 if (mFramebuffer) {
52 mWebGL->DeleteFramebuffer(mFramebuffer.get(), true);
53 mFramebuffer = nullptr;
57 /* static */
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
68 // these steps.
69 if (aSession.IsEnded()) {
70 aRv.ThrowInvalidStateError(
71 "Can not create an XRWebGLLayer with an XRSession that has ended.");
72 return nullptr;
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();
81 } else {
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.");
89 return nullptr;
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 "
100 "makeXRCompatible "
101 "on the WebGLRenderingContext or WebGL2RenderingContext.");
102 return nullptr;
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(
114 nsLiteralString(
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()) {
122 antialias = false;
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,
141 maxScale));
143 options.width =
144 (int32_t)ceilf(2.0f * displayState.eyeResolution.width * scaleFactor);
145 options.height =
146 (int32_t)ceilf(displayState.eyeResolution.height * scaleFactor);
147 framebuffer = gl->CreateOpaqueFramebuffer(options);
149 if (!framebuffer) {
150 aRv.ThrowOperationError(
151 "Could not create an XRWebGLLayer. XRFramebuffer creation failed.");
152 return nullptr;
154 framebufferOptions.emplace(options);
157 RefPtr<XRWebGLLayer> obj =
158 new XRWebGLLayer(aGlobal.GetAsSupports(), aSession, ignoreDepthValues,
159 aXRWebGLLayerInitDict.mFramebufferScaleFactor, gl,
160 framebuffer, framebufferOptions);
161 return obj.forget();
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.
195 return true;
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) {
225 rect.x = width;
227 RefPtr<XRViewport>& viewport =
228 aView.Eye() == XREye::Right ? mRightViewport : mLeftViewport;
229 if (!viewport) {
230 viewport = new XRViewport(mParent, rect);
231 } else {
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()) {
242 return 0.0f;
244 if (!aSession.IsImmersive()) {
245 return 1.0f;
248 const gfx::VRDisplayInfo& displayInfo =
249 aSession.GetDisplayClient()->GetDisplayInfo();
250 return displayInfo.mDisplayState.nativeFramebufferScaleFactor;
253 void XRWebGLLayer::StartAnimationFrame() {
254 if (mFramebuffer) {
255 mWebGL->SetFramebufferIsInOpaqueRAF(mFramebuffer.get(), true);
259 void XRWebGLLayer::EndAnimationFrame() {
260 if (mFramebuffer) {
261 mWebGL->SetFramebufferIsInOpaqueRAF(mFramebuffer.get(), false);
265 HTMLCanvasElement* XRWebGLLayer::GetCanvas() { return mWebGL->GetCanvas(); }
267 void XRWebGLLayer::SessionEnded() { DeleteFramebuffer(); }
269 } // namespace mozilla::dom