Title: [274695] trunk
Revision
274695
Author
[email protected]
Date
2021-03-18 18:27:38 -0700 (Thu, 18 Mar 2021)

Log Message

Implement WebXR Opaque Framebuffer
https://bugs.webkit.org/show_bug.cgi?id=222607

Reviewed by Youenn Fablet.

LayoutTests/imported/w3c:

Update WebXR Opaque Framebuffer test expectations.

* web-platform-tests/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt: Added.
* web-platform-tests/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt: Added.

Source/WebCore:

This patch adds a base XR layers API which is initially used by WebXRWebGLLayer, but
with the goal of supporting more types of layers in the future (see WebXR Layers spec).

Layers are connected to WebGL by using shared textures. In the case of WebXRWebGLLayer
it creates an Opaque Framebuffer that renders to the shared texture. XR runtimes supporting
double or triple buffering can send different shared textures each frame in the FrameData struct.
The texture for the current frame is attached in WebXRLayer::startFrame and prepared to send it back to the
VR compositor in WebXRLayer::endFrame().

Tested by the WebXR WPT tests.

* Modules/webxr/WebXRLayer.h: add startFrame and endFrame pure virtual methods.
* Modules/webxr/WebXRSession.cpp:
(WebCore::WebXRSession::onFrame): do not call RAFs if frameData.shouldRender is false.
* Modules/webxr/WebXRSession.h: add device getter.

* Modules/webxr/WebXRSystem.cpp:
(WebCore::WebXRSystem::DummyInlineDevice::requestFrame): always enable frameData.shouldRender flag.
* Modules/webxr/WebXRSystem.h: Override new virtual functions.
* Modules/webxr/WebXRViewport.h:
(WebCore::WebXRViewport::rect const): Add helper method.

* Modules/webxr/WebXRWebGLLayer.cpp: Refactor resource initialization to create method.
(WebCore::WebXRWebGLLayer::create):
(WebCore::WebXRWebGLLayer::WebXRWebGLLayer):
(WebCore::m_isCompositionEnabled):
(WebCore::WebXRWebGLLayer::framebuffer const):
(WebCore::WebXRWebGLLayer::framebufferWidth const):
(WebCore::WebXRWebGLLayer::framebufferHeight const):
(WebCore::WebXRWebGLLayer::startFrame): Prepare Opaque Framebuffer for the start of next frame.
(WebCore::WebXRWebGLLayer::endFrame): Prepare Opaque Framebuffer for VR compositor submitFrame.
(WebCore::WebXRWebGLLayer::canvasResized): Move method

(WebCore::WebXROpaqueFramebuffer::create): Implement class to manage WebXR Opaque framebuffer.
(WebCore::WebXROpaqueFramebuffer::WebXROpaqueFramebuffer):
(WebCore::WebXROpaqueFramebuffer::~WebXROpaqueFramebuffer):
(WebCore::WebXROpaqueFramebuffer::startFrame): Attach shared texture for the current frame.
(WebCore::WebXROpaqueFramebuffer::endFrame): Flush and resolve multisampling if required.
(WebCore::WebXROpaqueFramebuffer::setupFramebuffer): Allocate the Opaque framebuffer with requested attributes.

* Modules/webxr/WebXRWebGLLayer.h:  Add WebXROpaqueFramebuffer and handle members.
(WebCore::WebXROpaqueFramebuffer::handle const):
(WebCore::WebXROpaqueFramebuffer::framebuffer const):
(WebCore::WebXROpaqueFramebuffer::width const):
(WebCore::WebXROpaqueFramebuffer::height const):

* Sources.txt: Add OpenXRLayer.cpp and OpenXRSwapchain.cpp.

* html/canvas/WebGL2RenderingContext.cpp:
(WebCore::WebGL2RenderingContext::getFramebufferAttachmentParameter): handle Opaque Framebuffers.

* html/canvas/WebGLFramebuffer.cpp: Add members and methods required for Opaque Framebuffers.
(WebCore::WebGLFramebuffer::createOpaque):
(WebCore::WebGLFramebuffer::checkStatus const):
* html/canvas/WebGLFramebuffer.h:

* html/canvas/WebGLRenderingContext.cpp:
(WebCore::WebGLRenderingContext::getFramebufferAttachmentParameter): handle Opaque Framebuffers.

* html/canvas/WebGLRenderingContextBase.cpp: handle Opaque Framebuffers.
(WebCore::WebGLRenderingContextBase::deleteFramebuffer):
(WebCore::WebGLRenderingContextBase::framebufferRenderbuffer):
(WebCore::WebGLRenderingContextBase::framebufferTexture2D):

* platform/xr/PlatformXR.h: Add method to submitFrame.
(PlatformXR::Device::submitFrame):

* platform/xr/openxr/OpenXRLayer.cpp: Add class to manage OpenXR layers.
(PlatformXR::OpenXRLayer::OpenXRLayer):
(PlatformXR::OpenXRLayerProjection::create):
(PlatformXR::OpenXRLayerProjection::OpenXRLayerProjection):
(PlatformXR::OpenXRLayerProjection::startFrame):
* platform/xr/openxr/OpenXRLayer.h: Added.
(PlatformXR::OpenXRLayer::handle):

* platform/xr/openxr/OpenXRSwapchain.cpp: Add class to manage OpenXR swapchains
(PlatformXR::OpenXRSwapchain::create):
(PlatformXR::OpenXRSwapchain::OpenXRSwapchain):
(PlatformXR::OpenXRSwapchain::~OpenXRSwapchain):
(PlatformXR::OpenXRSwapchain::acquireImage):
(PlatformXR::OpenXRSwapchain::releaseImage):
* platform/xr/openxr/OpenXRSwapchain.h: Added.

* platform/xr/openxr/OpenXRUtils.h: Add helper log function
(PlatformXR::xrViewToPose): Use fabs instead of negation

* platform/xr/openxr/PlatformXROpenXR.cpp:
(PlatformXR::OpenXRDevice::initializeTrackingAndRendering): create GraphicsContextGL for texture sharing.
(PlatformXR::OpenXRDevice::requestFrame): Prepare and fill layers if shouldRender is true.
(PlatformXR::OpenXRDevice::submitFrame): Implement submit layers to OpenXR
(PlatformXR::OpenXRDevice::createLayerProjection): Implement layer creation method
(PlatformXR::OpenXRDevice::deleteLayer): Implement layer deallocation
(PlatformXR::OpenXRDevice::collectEnabledFeatures const):
(PlatformXR::OpenXRDevice::resetSession): Deallocate layers and graphics context.

* platform/xr/openxr/PlatformXROpenXR.h: Add Layers API

* testing/WebFakeXRDevice.cpp:
(WebCore::SimulatedXRDevice::recommendedResolution): Increase size to avoid platform minimum FBO size errors
(WebCore::SimulatedXRDevice::initializeTrackingAndRendering): Create graphics context
(WebCore::SimulatedXRDevice::shutDownTrackingAndRendering): Release graphics context
(WebCore::SimulatedXRDevice::frameTimerFired): set up layers in frame data
(WebCore::SimulatedXRDevice::createLayerProjection): creates a texture for a layer.
(WebCore::SimulatedXRDevice::deleteLayer): releases the associated texture to a layer
* testing/WebFakeXRDevice.h:

LayoutTests:

Update WebXR Opaque Framebuffer test expectations.

* platform/wpe/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (274694 => 274695)


--- trunk/LayoutTests/ChangeLog	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/LayoutTests/ChangeLog	2021-03-19 01:27:38 UTC (rev 274695)
@@ -1,3 +1,14 @@
+2021-03-18  Imanol Fernandez  <[email protected]>
+
+        Implement WebXR Opaque Framebuffer
+        https://bugs.webkit.org/show_bug.cgi?id=222607
+
+        Reviewed by Youenn Fablet.
+
+        Update WebXR Opaque Framebuffer test expectations.
+
+        * platform/wpe/TestExpectations:
+
 2021-03-18  Diego Pino Garcia  <[email protected]>
 
         [GLIB] REGRESSION(r274273): fast/canvas/webgl/gl-teximage-imagebitmap.html is failing

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (274694 => 274695)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2021-03-19 01:27:38 UTC (rev 274695)
@@ -1,3 +1,15 @@
+2021-03-18  Imanol Fernandez  <[email protected]>
+
+        Implement WebXR Opaque Framebuffer
+        https://bugs.webkit.org/show_bug.cgi?id=222607
+
+        Reviewed by Youenn Fablet.
+
+        Update WebXR Opaque Framebuffer test expectations.
+
+        * web-platform-tests/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt: Added.
+        * web-platform-tests/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt: Added.
+
 2021-03-18  Truitt Savell  <[email protected]>
 
         Unreviewed, reverting r274644.

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt (0 => 274695)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt	2021-03-19 01:27:38 UTC (rev 274695)
@@ -0,0 +1,7 @@
+CONSOLE MESSAGE: WebGL: INVALID_FRAMEBUFFER_OPERATION: clear: An opaque framebuffer is considered incomplete outside of a requestAnimationFrame
+CONSOLE MESSAGE: WebGL: INVALID_FRAMEBUFFER_OPERATION: clear: An opaque framebuffer is considered incomplete outside of a requestAnimationFrame
+CONSOLE MESSAGE: WebGL: INVALID_FRAMEBUFFER_OPERATION: drawArrays: An opaque framebuffer is considered incomplete outside of a requestAnimationFrame
+CONSOLE MESSAGE: WebGL: INVALID_FRAMEBUFFER_OPERATION: drawElements: An opaque framebuffer is considered incomplete outside of a requestAnimationFrame
+
+PASS Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt (0 => 274695)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt	2021-03-19 01:27:38 UTC (rev 274695)
@@ -0,0 +1,21 @@
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: deleteFramebuffer: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: getFramebufferAttachmentParameter: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: framebufferTexture2D: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: framebufferRenderbuffer: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: INVALID_OPERATION: framebufferRenderbuffer: An opaque framebuffer's attachments cannot be inspected or changed
+CONSOLE MESSAGE: WebGL: checkFramebufferStatus:An opaque framebuffer is considered incomplete outside of a requestAnimationFrame
+
+PASS Ensure that the framebuffer given by the WebGL layer is opaque for immersive
+PASS Ensure that the framebuffer given by the WebGL layer is opaque for non-immersive
+

Modified: trunk/LayoutTests/platform/wpe/TestExpectations (274694 => 274695)


--- trunk/LayoutTests/platform/wpe/TestExpectations	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/LayoutTests/platform/wpe/TestExpectations	2021-03-19 01:27:38 UTC (rev 274695)
@@ -654,7 +654,9 @@
 imported/w3c/web-platform-tests/webxr/xrSession_viewer_referenceSpace.https.html [ Pass ]
 imported/w3c/web-platform-tests/webxr/xrViewport_valid.https.html [ Pass ]
 imported/w3c/web-platform-tests/webxr/xrWebGLLayer_constructor.https.html [ Pass ]
+imported/w3c/web-platform-tests/webxr/xrWebGLLayer_framebuffer_draw.https.html [ Pass ]
 imported/w3c/web-platform-tests/webxr/xrWebGLLayer_framebuffer_sameObject.https.html [ Pass ]
+imported/w3c/web-platform-tests/webxr/xrWebGLLayer_opaque_framebuffer.https.html [ Pass ]
 imported/w3c/web-platform-tests/webxr/xrWebGLLayer_viewports.https.html [ Pass ]
 imported/w3c/web-platform-tests/webxr/render_state_vertical_fov_immersive.https.html [ Pass ]
 imported/w3c/web-platform-tests/webxr/render_state_update.https.html [ Pass ]

Modified: trunk/Source/WebCore/ChangeLog (274694 => 274695)


--- trunk/Source/WebCore/ChangeLog	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/ChangeLog	2021-03-19 01:27:38 UTC (rev 274695)
@@ -1,3 +1,116 @@
+2021-03-18  Imanol Fernandez  <[email protected]>
+
+        Implement WebXR Opaque Framebuffer
+        https://bugs.webkit.org/show_bug.cgi?id=222607
+
+        Reviewed by Youenn Fablet.
+
+        This patch adds a base XR layers API which is initially used by WebXRWebGLLayer, but
+        with the goal of supporting more types of layers in the future (see WebXR Layers spec).
+
+        Layers are connected to WebGL by using shared textures. In the case of WebXRWebGLLayer
+        it creates an Opaque Framebuffer that renders to the shared texture. XR runtimes supporting
+        double or triple buffering can send different shared textures each frame in the FrameData struct.
+        The texture for the current frame is attached in WebXRLayer::startFrame and prepared to send it back to the
+        VR compositor in WebXRLayer::endFrame().
+
+        Tested by the WebXR WPT tests.
+
+        * Modules/webxr/WebXRLayer.h: add startFrame and endFrame pure virtual methods.
+        * Modules/webxr/WebXRSession.cpp:
+        (WebCore::WebXRSession::onFrame): do not call RAFs if frameData.shouldRender is false.
+        * Modules/webxr/WebXRSession.h: add device getter.
+
+        * Modules/webxr/WebXRSystem.cpp:
+        (WebCore::WebXRSystem::DummyInlineDevice::requestFrame): always enable frameData.shouldRender flag.
+        * Modules/webxr/WebXRSystem.h: Override new virtual functions.
+        * Modules/webxr/WebXRViewport.h:
+        (WebCore::WebXRViewport::rect const): Add helper method.
+
+        * Modules/webxr/WebXRWebGLLayer.cpp: Refactor resource initialization to create method.
+        (WebCore::WebXRWebGLLayer::create):
+        (WebCore::WebXRWebGLLayer::WebXRWebGLLayer):
+        (WebCore::m_isCompositionEnabled):
+        (WebCore::WebXRWebGLLayer::framebuffer const):
+        (WebCore::WebXRWebGLLayer::framebufferWidth const):
+        (WebCore::WebXRWebGLLayer::framebufferHeight const):
+        (WebCore::WebXRWebGLLayer::startFrame): Prepare Opaque Framebuffer for the start of next frame.
+        (WebCore::WebXRWebGLLayer::endFrame): Prepare Opaque Framebuffer for VR compositor submitFrame.
+        (WebCore::WebXRWebGLLayer::canvasResized): Move method
+
+        (WebCore::WebXROpaqueFramebuffer::create): Implement class to manage WebXR Opaque framebuffer.
+        (WebCore::WebXROpaqueFramebuffer::WebXROpaqueFramebuffer):
+        (WebCore::WebXROpaqueFramebuffer::~WebXROpaqueFramebuffer):
+        (WebCore::WebXROpaqueFramebuffer::startFrame): Attach shared texture for the current frame.
+        (WebCore::WebXROpaqueFramebuffer::endFrame): Flush and resolve multisampling if required.
+        (WebCore::WebXROpaqueFramebuffer::setupFramebuffer): Allocate the Opaque framebuffer with requested attributes.
+
+        * Modules/webxr/WebXRWebGLLayer.h:  Add WebXROpaqueFramebuffer and handle members.
+        (WebCore::WebXROpaqueFramebuffer::handle const):
+        (WebCore::WebXROpaqueFramebuffer::framebuffer const):
+        (WebCore::WebXROpaqueFramebuffer::width const):
+        (WebCore::WebXROpaqueFramebuffer::height const):
+
+        * Sources.txt: Add OpenXRLayer.cpp and OpenXRSwapchain.cpp.
+
+        * html/canvas/WebGL2RenderingContext.cpp:
+        (WebCore::WebGL2RenderingContext::getFramebufferAttachmentParameter): handle Opaque Framebuffers.
+
+        * html/canvas/WebGLFramebuffer.cpp: Add members and methods required for Opaque Framebuffers.
+        (WebCore::WebGLFramebuffer::createOpaque):
+        (WebCore::WebGLFramebuffer::checkStatus const):
+        * html/canvas/WebGLFramebuffer.h:
+
+        * html/canvas/WebGLRenderingContext.cpp:
+        (WebCore::WebGLRenderingContext::getFramebufferAttachmentParameter): handle Opaque Framebuffers.
+
+        * html/canvas/WebGLRenderingContextBase.cpp: handle Opaque Framebuffers.
+        (WebCore::WebGLRenderingContextBase::deleteFramebuffer):
+        (WebCore::WebGLRenderingContextBase::framebufferRenderbuffer):
+        (WebCore::WebGLRenderingContextBase::framebufferTexture2D):
+
+        * platform/xr/PlatformXR.h: Add method to submitFrame.
+        (PlatformXR::Device::submitFrame):
+
+        * platform/xr/openxr/OpenXRLayer.cpp: Add class to manage OpenXR layers.
+        (PlatformXR::OpenXRLayer::OpenXRLayer):
+        (PlatformXR::OpenXRLayerProjection::create):
+        (PlatformXR::OpenXRLayerProjection::OpenXRLayerProjection):
+        (PlatformXR::OpenXRLayerProjection::startFrame):
+        * platform/xr/openxr/OpenXRLayer.h: Added.
+        (PlatformXR::OpenXRLayer::handle):
+
+        * platform/xr/openxr/OpenXRSwapchain.cpp: Add class to manage OpenXR swapchains
+        (PlatformXR::OpenXRSwapchain::create):
+        (PlatformXR::OpenXRSwapchain::OpenXRSwapchain):
+        (PlatformXR::OpenXRSwapchain::~OpenXRSwapchain):
+        (PlatformXR::OpenXRSwapchain::acquireImage):
+        (PlatformXR::OpenXRSwapchain::releaseImage):
+        * platform/xr/openxr/OpenXRSwapchain.h: Added.
+
+        * platform/xr/openxr/OpenXRUtils.h: Add helper log function
+        (PlatformXR::xrViewToPose): Use fabs instead of negation
+
+        * platform/xr/openxr/PlatformXROpenXR.cpp:
+        (PlatformXR::OpenXRDevice::initializeTrackingAndRendering): create GraphicsContextGL for texture sharing.
+        (PlatformXR::OpenXRDevice::requestFrame): Prepare and fill layers if shouldRender is true.
+        (PlatformXR::OpenXRDevice::submitFrame): Implement submit layers to OpenXR
+        (PlatformXR::OpenXRDevice::createLayerProjection): Implement layer creation method
+        (PlatformXR::OpenXRDevice::deleteLayer): Implement layer deallocation
+        (PlatformXR::OpenXRDevice::collectEnabledFeatures const):
+        (PlatformXR::OpenXRDevice::resetSession): Deallocate layers and graphics context.
+
+        * platform/xr/openxr/PlatformXROpenXR.h: Add Layers API
+
+        * testing/WebFakeXRDevice.cpp:
+        (WebCore::SimulatedXRDevice::recommendedResolution): Increase size to avoid platform minimum FBO size errors
+        (WebCore::SimulatedXRDevice::initializeTrackingAndRendering): Create graphics context
+        (WebCore::SimulatedXRDevice::shutDownTrackingAndRendering): Release graphics context
+        (WebCore::SimulatedXRDevice::frameTimerFired): set up layers in frame data
+        (WebCore::SimulatedXRDevice::createLayerProjection): creates a texture for a layer.
+        (WebCore::SimulatedXRDevice::deleteLayer): releases the associated texture to a layer
+        * testing/WebFakeXRDevice.h:
+
 2021-03-18  Wenson Hsieh  <[email protected]>
 
         Avoid copying item data when iterating in-process display lists

Modified: trunk/Source/WebCore/Modules/webxr/WebXRLayer.h (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRLayer.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRLayer.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -29,6 +29,7 @@
 
 #include "ContextDestructionObserver.h"
 #include "EventTarget.h"
+#include "PlatformXR.h"
 #include <wtf/IsoMalloc.h>
 #include <wtf/RefCounted.h>
 
@@ -44,6 +45,9 @@
     using RefCounted<WebXRLayer>::ref;
     using RefCounted<WebXRLayer>::deref;
 
+    virtual void startFrame(const PlatformXR::Device::FrameData&) = 0;
+    virtual PlatformXR::Device::Layer endFrame() = 0;
+
 protected:
     explicit WebXRLayer(ScriptExecutionContext*);
 

Modified: trunk/Source/WebCore/Modules/webxr/WebXRSession.cpp (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRSession.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSession.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -514,7 +514,11 @@
             applyPendingRenderState();
 
         // 6. If the frame should be rendered for session:
-        if (frameShouldBeRendered()) {
+        if (frameShouldBeRendered() && m_frameData.shouldRender) {
+            // Prepare all layers for render
+            if (m_mode == XRSessionMode::ImmersiveVr && m_activeRenderState->baseLayer())
+                m_activeRenderState->baseLayer()->startFrame(m_frameData);
+
             // 6.1.Set session’s list of currently running animation frame callbacks to be session’s list of animation frame callbacks.
             // 6.2.Set session’s list of animation frame callbacks to the empty list.
             auto callbacks = m_callbacks;
@@ -545,10 +549,18 @@
             // If the session is ended, m_animationFrame->setActive false is set in shutdown().
             frame->setActive(false);
 
-            if (!m_callbacks.isEmpty())
-                requestFrame();
+
+            // Submit current frame layers to the device.
+            Vector<PlatformXR::Device::Layer> frameLayers;
+            if (m_mode == XRSessionMode::ImmersiveVr && m_activeRenderState->baseLayer())
+                frameLayers.append(m_activeRenderState->baseLayer()->endFrame());
+
+            m_device->submitFrame(WTFMove(frameLayers));
         }
 
+        if (!m_callbacks.isEmpty())
+            requestFrame();
+
     });
 }
 

Modified: trunk/Source/WebCore/Modules/webxr/WebXRSession.h (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRSession.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSession.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -73,6 +73,7 @@
     XRVisibilityState visibilityState() const;
     const WebXRRenderState& renderState() const;
     const WebXRInputSourceArray& inputSources() const;
+    PlatformXR::Device* device() const { return m_device.get(); }
 
     ExceptionOr<void> updateRenderState(const XRRenderStateInit&);
     void requestReferenceSpace(XRReferenceSpaceType, RequestReferenceSpacePromise&&);

Modified: trunk/Source/WebCore/Modules/webxr/WebXRSystem.cpp (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRSystem.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSystem.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -534,6 +534,7 @@
         PlatformXR::Device::FrameData data;
         data.isTrackingValid = true;
         data.isPositionValid = true;
+        data.shouldRender = true;
         data.views.append({ });
         callback(WTFMove(data));
     });

Modified: trunk/Source/WebCore/Modules/webxr/WebXRSystem.h (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRSystem.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSystem.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -115,6 +115,8 @@
 
         void requestFrame(PlatformXR::Device::RequestFrameCallback&&) final;
         Vector<Device::ViewData> views(XRSessionMode) const final;
+        Optional<PlatformXR::LayerHandle> createLayerProjection(uint32_t, uint32_t, bool) final { return WTF::nullopt; }
+        void deleteLayer(PlatformXR::LayerHandle) final { }
     };
     DummyInlineDevice m_defaultInlineDevice;
 

Modified: trunk/Source/WebCore/Modules/webxr/WebXRViewport.h (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRViewport.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRViewport.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -43,6 +43,7 @@
     int y() const { return m_viewport.y(); }
     int width() const { return m_viewport.width(); }
     int height() const { return m_viewport.height(); }
+    IntRect rect() { return m_viewport; }
 
     void updateViewport(const IntRect& viewport) { m_viewport = viewport; }
 

Modified: trunk/Source/WebCore/Modules/webxr/WebXRWebGLLayer.cpp (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRWebGLLayer.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRWebGLLayer.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -28,9 +28,21 @@
 
 #if ENABLE(WEBXR)
 
+#if !USE(ANGLE)
+#include "ExtensionsGLOpenGLCommon.h"
+#endif
+#if USE(OPENGL_ES)
+#include "ExtensionsGLOpenGLES.h"
+#endif
+#if !USE(ANGLE)
+#include "GraphicsContextGL.h"
+#endif
 #include "HTMLCanvasElement.h"
 #include "IntSize.h"
 #include "OffscreenCanvas.h"
+#if !USE(ANGLE)
+#include "TemporaryOpenGLSetting.h"
+#endif
 #include "WebGLFramebuffer.h"
 #include "WebGLRenderingContext.h"
 #if ENABLE(WEBGL2)
@@ -47,8 +59,52 @@
 
 namespace WebCore {
 
+using GL = GraphicsContextGL;
+
 WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRWebGLLayer);
 
+// Arbitrary value for minimum framebuffer scaling.
+// Below this threshold the resulting framebuffer would be too small to see.
+constexpr double MinFramebufferScalingFactor = 0.2;
+
+static ExceptionOr<std::unique_ptr<WebXROpaqueFramebuffer>> createOpaqueFramebuffer(WebXRSession& session, WebGLRenderingContextBase& context, const XRWebGLLayerInit& init)
+{
+    auto device = session.device();
+    if (!device)
+        return Exception { OperationError, "Cannot create an XRWebGLLayer with an XRSession that has ended." };
+
+    // 9.1. Initialize layer’s antialias to layerInit’s antialias value.
+    // 9.2. Let framebufferSize be the recommended WebGL framebuffer resolution multiplied by layerInit's framebufferScaleFactor.
+
+    float scaleFactor = std::clamp(init.framebufferScaleFactor, MinFramebufferScalingFactor, device->maxFramebufferScalingFactor());
+
+    IntSize recommendedSize = session.recommendedWebGLFramebufferResolution();
+    auto width = static_cast<uint32_t>(std::ceil(2 * recommendedSize.width() * scaleFactor));
+    auto height = static_cast<uint32_t>(std::ceil(recommendedSize.height() * scaleFactor));
+
+    // 9.3. Initialize layer’s framebuffer to a new opaque framebuffer with the dimensions framebufferSize
+    //      created with context, session initialized to session, and layerInit’s depth, stencil, and alpha values.
+    // 9.4. Allocate and initialize resources compatible with session’s XR device, including GPU accessible memory buffers,
+    //      as required to support the compositing of layer.
+    // 9.5. If layer’s resources were unable to be created for any reason, throw an OperationError and abort these steps.
+    auto layerHandle = device->createLayerProjection(width, height, init.alpha);
+    if (!layerHandle)
+        return Exception { OperationError, "Unable to allocate XRWebGLLayer GPU resources."};
+
+    WebXROpaqueFramebuffer::Attributes attributes {
+        .alpha = init.alpha,
+        .antialias = init.antialias,
+        .depth = init.depth,
+        .stencil = init.stencil
+    };
+
+    auto framebuffer = WebXROpaqueFramebuffer::create(*layerHandle, context, WTFMove(attributes), width, height);
+    if (!framebuffer)
+        return Exception { OperationError, "Unable to create a framebuffer." };
+    
+    return framebuffer;
+}
+
 // https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
 ExceptionOr<Ref<WebXRWebGLLayer>> WebXRWebGLLayer::create(Ref<WebXRSession>&& session, WebXRRenderingContext&& context, const XRWebGLLayerInit& init)
 {
@@ -55,7 +111,7 @@
     // 1. Let layer be a new XRWebGLLayer
     // 2. If session’s ended value is true, throw an InvalidStateError and abort these steps.
     if (session->ended())
-        return Exception { InvalidStateError };
+        return Exception { InvalidStateError, "Cannot create an XRWebGLLayer with an XRSession that has ended." };
 
     // 3. If context is lost, throw an InvalidStateError and abort these steps.
     // 4. If session is an immersive session and context’s XR compatible boolean is false, throw
@@ -64,29 +120,44 @@
         [&](const RefPtr<WebGLRenderingContextBase>& baseContext) -> ExceptionOr<Ref<WebXRWebGLLayer>>
         {
             if (baseContext->isContextLost())
-                return Exception { InvalidStateError };
+                return Exception { InvalidStateError, "Cannot create an XRWebGLLayer with a lost WebGL context." };
 
             auto mode = session->mode();
             if ((mode == XRSessionMode::ImmersiveAr || mode == XRSessionMode::ImmersiveVr) && !baseContext->isXRCompatible())
-                return Exception { InvalidStateError };
+                return Exception { InvalidStateError, "Cannot create an XRWebGLLayer with WebGL context not marked as XR compatible." };
 
 
             // 5. Initialize layer’s context to context. (see constructor)
             // 6. Initialize layer’s session to session. (see constructor)
             // 7. Initialize layer’s ignoreDepthValues as follows. (see constructor)
-            // 8. Initialize layer’s composition disabled boolean as follows. (see constructor)
-            // 9. (see constructor except for the resources initialization step which is handled in the if block below)
-            auto layer = adoptRef(*new WebXRWebGLLayer(WTFMove(session), WTFMove(context), init));
+            //   7.1 If layerInit’s ignoreDepthValues value is false and the XR Compositor will make use of depth values,
+            //       Initialize layer’s ignoreDepthValues to false.
+            //   7.2. Else Initialize layer’s ignoreDepthValues to true
+            // TODO: ask XR compositor for depth value usages support
+            bool ignoreDepthValues = true;
+            // 8. Initialize layer’s composition disabled boolean as follows.
+            //    If session is an inline session -> Initialize layer's composition disabled to true
+            //    Otherwise -> Initialize layer's composition disabled boolean to false
+            bool isCompositionEnabled = session->mode() != XRSessionMode::Inline;
+            bool antialias = false;
+            std::unique_ptr<WebXROpaqueFramebuffer> framebuffer;
 
-            if (layer->m_isCompositionEnabled) {
-                // 9.4. Allocate and initialize resources compatible with session’s XR device, including GPU accessible memory buffers,
-                //      as required to support the compositing of layer.
-                // 9.5. If layer’s resources were unable to be created for any reason, throw an OperationError and abort these steps.
-                // TODO: Initialize layer's resources or issue an OperationError.
+            // 9. If layer's composition enabled boolean is true: 
+            if (isCompositionEnabled) {
+                auto createResult = createOpaqueFramebuffer(session.get(), *baseContext, init);
+                if (createResult.hasException())
+                    return createResult.releaseException();
+                framebuffer = createResult.releaseReturnValue();
+            } else {
+                // Otherwise
+                // 9.1. Initialize layer’s antialias to layer’s context's actual context parameters antialias value.
+                if (auto attributes = baseContext->getContextAttributes())
+                    antialias = attributes->antialias;
+                // 9.2 Initialize layer's framebuffer to null.
             }
 
             // 10. Return layer.
-            return layer;
+            return adoptRef(*new WebXRWebGLLayer(WTFMove(session), WTFMove(context), WTFMove(framebuffer), antialias, ignoreDepthValues, isCompositionEnabled));
         },
         [](WTF::Monostate) {
             ASSERT_NOT_REACHED();
@@ -95,63 +166,18 @@
     );
 }
 
-WebXRWebGLLayer::WebXRWebGLLayer(Ref<WebXRSession>&& session, WebXRRenderingContext&& context, const XRWebGLLayerInit& init)
+WebXRWebGLLayer::WebXRWebGLLayer(Ref<WebXRSession>&& session, WebXRRenderingContext&& context, std::unique_ptr<WebXROpaqueFramebuffer>&& framebuffer,
+    bool antialias, bool ignoreDepthValues, bool isCompositionEnabled)
     : WebXRLayer(session->scriptExecutionContext())
     , m_session(WTFMove(session))
     , m_context(WTFMove(context))
     , m_leftViewportData({ WebXRViewport::create({ }) })
     , m_rightViewportData({ WebXRViewport::create({ }) })
+    , m_framebuffer(WTFMove(framebuffer))
+    , m_antialias(antialias)
+    , m_ignoreDepthValues(ignoreDepthValues)
+    , m_isCompositionEnabled(isCompositionEnabled)
 {
-    // 7. Initialize layer’s ignoreDepthValues as follows:
-    //   7.1 If layerInit’s ignoreDepthValues value is false and the XR Compositor will make use of depth values,
-    //       Initialize layer’s ignoreDepthValues to false.
-    //   7.2. Else Initialize layer’s ignoreDepthValues to true
-    // TODO: ask XR compositor for depth value usages
-    m_ignoreDepthValues = init.ignoreDepthValues;
-
-    // 8. Initialize layer's composition disabled boolean as follows:
-    //  If session is an inline session -> Initialize layer's composition disabled to true
-    //  Otherwise -> Initialize layer's composition disabled boolean to false
-    m_isCompositionEnabled = m_session->mode() != XRSessionMode::Inline;
-
-    // 9. If layer’s composition disabled boolean is false:
-    if (m_isCompositionEnabled) {
-        //  1. Initialize layer’s antialias to layerInit’s antialias value.
-        m_antialias = init.antialias;
-
-        //  2. Let framebufferSize be the recommended WebGL framebuffer resolution multiplied by layerInit's framebufferScaleFactor.
-        IntSize recommendedSize = m_session->recommendedWebGLFramebufferResolution();
-        m_framebuffer.width = recommendedSize.width() * init.framebufferScaleFactor;
-        m_framebuffer.height = recommendedSize.height() * init.framebufferScaleFactor;
-
-        //  3. Initialize layer’s framebuffer to a new opaque framebuffer with the dimensions framebufferSize
-        //       created with context, session initialized to session, and layerInit’s depth, stencil, and alpha values.
-        // For steps 4 & 5 see the create() method as resources initialization is handled outside the constructor as it might trigger
-        // an OperationError.
-        // FIXME: create a proper opaque framebuffer.
-        m_framebuffer.object = WTF::switchOn(m_context,
-            [](const RefPtr<WebGLRenderingContextBase>& baseContext)
-            {
-                return WebGLFramebuffer::create(*baseContext);
-            }
-        );
-    } else {
-        // 1. Initialize layer’s antialias to layer’s context's actual context parameters antialias value.
-        m_antialias = WTF::switchOn(m_context,
-            [](const RefPtr<WebGLRenderingContextBase>& context)
-            {
-                if (auto attributes = context->getContextAttributes())
-                    return attributes.value().antialias;
-                return false;
-            }
-        );
-        // 2. Initialize layer’s framebuffer to null.
-        m_framebuffer.object = nullptr;
-    }
-
-    auto canvasElement = canvas();
-    if (canvasElement)
-        canvasElement->addObserver(*this);
 }
 
 WebXRWebGLLayer::~WebXRWebGLLayer()
@@ -159,6 +185,11 @@
     auto canvasElement = canvas();
     if (canvasElement)
         canvasElement->removeObserver(*this);
+    if (m_framebuffer) {
+        auto device = m_session->device();
+        if (device)
+            device->deleteLayer(m_framebuffer->handle());
+    }
 }
 
 bool WebXRWebGLLayer::antialias() const
@@ -171,15 +202,15 @@
     return m_ignoreDepthValues;
 }
 
-WebGLFramebuffer* WebXRWebGLLayer::framebuffer() const
+const WebGLFramebuffer* WebXRWebGLLayer::framebuffer() const
 {
-    return m_framebuffer.object.get();
+    return m_framebuffer ? &m_framebuffer->framebuffer() : nullptr;
 }
 
 unsigned WebXRWebGLLayer::framebufferWidth() const
 {
-    if (m_framebuffer.object)
-        return m_framebuffer.width;
+    if (m_framebuffer)
+        return m_framebuffer->width();
     return WTF::switchOn(m_context,
         [&](const RefPtr<WebGLRenderingContextBase>& baseContext) {
             return baseContext->drawingBufferWidth();
@@ -188,8 +219,8 @@
 
 unsigned WebXRWebGLLayer::framebufferHeight() const
 {
-    if (m_framebuffer.object)
-        return m_framebuffer.height;
+    if (m_framebuffer)
+        return m_framebuffer->height();
     return WTF::switchOn(m_context,
         [&](const RefPtr<WebGLRenderingContextBase>& baseContext) {
             return baseContext->drawingBufferHeight();
@@ -256,6 +287,39 @@
     });
 }
 
+
+void WebXRWebGLLayer::startFrame(const PlatformXR::Device::FrameData& data)
+{
+    ASSERT(m_framebuffer);
+
+    auto it = data.layers.find(m_framebuffer->handle());
+    if (it == data.layers.end()) {
+        // For some reason the device didn't provide a texture for this frame.
+        // The frame is ignored and the device can recover the texture in future frames;
+        return;
+    }
+
+    m_framebuffer->startFrame(it->value);
+}
+
+PlatformXR::Device::Layer WebXRWebGLLayer::endFrame()
+{
+    ASSERT(m_framebuffer);
+    m_framebuffer->endFrame();
+
+    Vector<PlatformXR::Device::LayerView> views {
+        { PlatformXR::Eye::Left, m_leftViewportData.viewport->rect() },
+        { PlatformXR::Eye::Right, m_rightViewportData.viewport->rect() }
+    };
+
+    return PlatformXR::Device::Layer { .handle = m_framebuffer->handle(), .visible = true, .views = WTFMove(views) };
+}
+
+void WebXRWebGLLayer::canvasResized(CanvasBase&)
+{
+    m_viewportsDirty = true;
+}
+
 // https://immersive-web.github.io/webxr/#xrview-obtain-a-scaled-viewport
 void WebXRWebGLLayer::computeViewports()
 {
@@ -283,11 +347,260 @@
     m_viewportsDirty = false;
 }
 
-void WebXRWebGLLayer::canvasResized(CanvasBase&)
+// WebXROpaqueFramebuffer
+
+std::unique_ptr<WebXROpaqueFramebuffer> WebXROpaqueFramebuffer::create(PlatformXR::LayerHandle handle, WebGLRenderingContextBase& context, Attributes&& attributes, uint32_t width, uint32_t height)
 {
-    m_viewportsDirty = true;
+    auto framebuffer = WebGLFramebuffer::createOpaque(context);
+    auto opaque = std::unique_ptr<WebXROpaqueFramebuffer>(new WebXROpaqueFramebuffer(handle, WTFMove(framebuffer), context, WTFMove(attributes), width, height));
+    if (!opaque->setupFramebuffer())
+        return nullptr;
+    return opaque;
 }
 
+WebXROpaqueFramebuffer::WebXROpaqueFramebuffer(PlatformXR::LayerHandle handle, Ref<WebGLFramebuffer>&& framebuffer, WebGLRenderingContextBase& context, Attributes&& attributes, uint32_t width, uint32_t height)
+    : m_handle(handle)
+    , m_framebuffer(WTFMove(framebuffer))
+    , m_context(context)
+    , m_attributes(WTFMove(attributes))
+    , m_width(width)
+    , m_height(height)
+{
+}
+
+WebXROpaqueFramebuffer::~WebXROpaqueFramebuffer()
+{
+    if (auto gl = m_context.graphicsContextGL()) {
+        if (m_stencilBuffer)
+            gl->deleteRenderbuffer(m_stencilBuffer);
+        if (m_depthStencilBuffer)
+            gl->deleteRenderbuffer(m_depthStencilBuffer);
+        if (m_multisampleColorBuffer)
+            gl->deleteRenderbuffer(m_multisampleColorBuffer);
+        if (m_resolvedFBO)
+            gl->deleteFramebuffer(m_resolvedFBO);
+        m_context.deleteFramebuffer(m_framebuffer.ptr());
+    }
+}
+
+void WebXROpaqueFramebuffer::startFrame(const PlatformXR::Device::FrameData::LayerData& data)
+{
+    m_opaqueTexture = data.opaqueTexture;
+    if (!m_context.graphicsContextGL())
+        return;
+    auto& gl = *m_context.graphicsContextGL();
+
+    m_framebuffer->setOpaqueActive(true);
+
+    GCGLint boundFBO { 0 };
+
+    gl.getIntegerv(GL::FRAMEBUFFER_BINDING, makeGCGLSpan(&boundFBO, 1));
+    auto scopedFBOs = makeScopeExit([&gl, boundFBO]() {
+        gl.bindFramebuffer(GL::FRAMEBUFFER, boundFBO);
+    });
+
+    gl.bindFramebuffer(GL::FRAMEBUFFER, m_framebuffer->object());
+    // https://immersive-web.github.io/webxr/#opaque-framebuffer
+    // The buffers attached to an opaque framebuffer MUST be cleared to the values in the table below when first created,
+    // or prior to the processing of each XR animation frame.
+    std::array<const GCGLenum, 3> attachments = { GL::COLOR_ATTACHMENT0, GL::STENCIL_ATTACHMENT, GL::DEPTH_ATTACHMENT };
+    gl.invalidateFramebuffer(GL::FRAMEBUFFER, makeGCGLSpan(attachments.data(), attachments.size()));
+
+#if USE(OPENGL_ES)
+    auto& extensions = reinterpret_cast<ExtensionsGLOpenGLES&>(gl.getExtensions());
+    if (m_attributes.antialias && extensions.isImagination()) {
+        extensions.framebufferTexture2DMultisampleIMG(GL::FRAMEBUFFER, GL::COLOR_ATTACHMENT0, GL::TEXTURE_2D, m_opaqueTexture, 0, m_sampleCount);
+        return;
+    }
+#endif
+
+    if (!m_multisampleColorBuffer)
+        gl.framebufferTexture2D(GL::FRAMEBUFFER, GL::COLOR_ATTACHMENT0, GL::TEXTURE_2D, m_opaqueTexture, 0);
+}
+
+void WebXROpaqueFramebuffer::endFrame()
+{
+    m_framebuffer->setOpaqueActive(false);
+
+    if (!m_context.graphicsContextGL())
+        return;
+    auto& gl = *m_context.graphicsContextGL();
+
+    if (m_multisampleColorBuffer) {
+#if !USE(ANGLE)
+        // FIXME: These may be needed when using ANGLE, but it didn't compile in the initial implementation.
+        TemporaryOpenGLSetting scopedScissor(GL::SCISSOR_TEST, 0);
+        TemporaryOpenGLSetting scopedDither(GL::DITHER, 0);
+        TemporaryOpenGLSetting scopedDepth(GL::DEPTH_TEST, 0);
+        TemporaryOpenGLSetting scopedStencil(GL::STENCIL_TEST, 0);
+#endif
+
+        GCGLint boundReadFBO { 0 };
+        GCGLint boundDrawFBO { 0 };
+        gl.getIntegerv(GL::READ_FRAMEBUFFER_BINDING, makeGCGLSpan(&boundReadFBO, 1));
+        gl.getIntegerv(GL::DRAW_FRAMEBUFFER_BINDING, makeGCGLSpan(&boundDrawFBO, 1));
+
+        auto scopedFBOs = makeScopeExit([&gl, boundReadFBO, boundDrawFBO]() {
+            gl.bindFramebuffer(GL::READ_FRAMEBUFFER, boundReadFBO);
+            gl.bindFramebuffer(GL::DRAW_FRAMEBUFFER, boundDrawFBO);
+        });
+
+        gl.bindFramebuffer(GL::DRAW_FRAMEBUFFER, m_resolvedFBO);
+        gl.framebufferTexture2D(GL::FRAMEBUFFER, GL::COLOR_ATTACHMENT0, GL::TEXTURE_2D, m_opaqueTexture, 0);
+
+        // Resolve multisample framebuffer
+        gl.bindFramebuffer(GL::READ_FRAMEBUFFER, m_framebuffer->object());
+        gl.blitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL::COLOR_BUFFER_BIT, GL::LINEAR);
+    }
+    
+    gl.flush();
+}
+
+bool WebXROpaqueFramebuffer::setupFramebuffer()
+{
+    if (!m_context.graphicsContextGL())
+        return false;
+    auto& gl = *m_context.graphicsContextGL();
+
+    // Bind current FBOs when exiting the function
+    GCGLint boundFBO { 0 };
+    GCGLint boundRenderbuffer { 0 };
+    gl.getIntegerv(GL::FRAMEBUFFER_BINDING, makeGCGLSpan(&boundFBO, 1));
+    gl.getIntegerv(GL::RENDERBUFFER_BINDING, makeGCGLSpan(&boundRenderbuffer, 1));
+    auto scopedFBO = makeScopeExit([&gl, boundFBO, boundRenderbuffer]() {
+        gl.bindFramebuffer(GL::FRAMEBUFFER, boundFBO);
+        gl.bindRenderbuffer(GL::RENDERBUFFER, boundRenderbuffer);
+    });
+
+    // Set up color, depth and stencil formats
+    bool useDepthStencil = m_attributes.stencil || m_attributes.depth;
+    auto colorFormat = m_attributes.alpha ? GraphicsContextGL::RGBA8 : GraphicsContextGL::RGB8;
+#if USE(OPENGL_ES)
+    auto& extensions = reinterpret_cast<ExtensionsGLOpenGLES&>(gl.getExtensions());
+    bool supportsPackedDepthStencil = useDepthStencil && extensions.supports("GL_OES_packed_depth_stencil");
+    auto depthFormat = supportsPackedDepthStencil ? GL::DEPTH24_STENCIL8 : GL::DEPTH_COMPONENT16;
+    auto stencilFormat = GL::STENCIL_INDEX8;
+#elif USE(ANGLE)
+    // FIXME: These values were chosen just to get this to compile successfully.
+    // Make sure they are correct.
+    bool supportsPackedDepthStencil = false;
+    auto depthFormat = supportsPackedDepthStencil ? GL::DEPTH24_STENCIL8 : GL::DEPTH_COMPONENT;
+    auto stencilFormat = GL::STENCIL_INDEX8;
+#else
+    auto& extensions = reinterpret_cast<ExtensionsGLOpenGLCommon&>(gl.getExtensions());
+    bool supportsPackedDepthStencil = useDepthStencil && extensions.supports("GL_EXT_packed_depth_stencil");
+    auto depthFormat = supportsPackedDepthStencil ? GL::DEPTH24_STENCIL8 : GL::DEPTH_COMPONENT;
+    auto stencilFormat = GL::STENCIL_COMPONENT;
+#endif
+
+    // Set up recommended samples for WebXR.
+    // FIXME: check if we can get recommended values from each device platform.
+    if (m_attributes.antialias) {
+        GCGLint maxSampleCount;
+#if USE(ANGLE)
+        // FIXME: This probably is not correct.
+        maxSampleCount = 0;
+#else
+        gl.getIntegerv(ExtensionsGL::MAX_SAMPLES, makeGCGLSpan(&maxSampleCount, 1));
+#endif
+        // Using more than 4 samples might be overhead.
+        m_sampleCount = std::min(4, maxSampleCount);
+    }
+
+#if USE(OPENGL_ES)
+    // Use multisampled_render_to_texture extension if available.
+    if (m_attributes.antialias && extensions.isImagination()) {
+        // framebufferTexture2DMultisampleIMG is set up in startFrame call.
+        if (!useDepthStencil)
+            return true;
+        
+        gl.bindFramebuffer(GL::FRAMEBUFFER, m_framebuffer->object());
+        m_depthStencilBuffer = gl.createRenderbuffer();
+        if (supportsPackedDepthStencil) {
+            gl.bindRenderbuffer(GL::RENDERBUFFER, m_depthStencilBuffer);
+            extensions.renderbufferStorageMultisampleANGLE(GL::RENDERBUFFER, m_sampleCount, depthFormat, m_width, m_height);
+            if (m_attributes.stencil)
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::STENCIL_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+            if (m_attributes.depth)
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::DEPTH_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+        } else {
+            if (m_attributes.stencil) {
+                m_stencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(GL::RENDERBUFFER, m_stencilBuffer);
+                extensions.renderbufferStorageMultisampleANGLE(GL::RENDERBUFFER, m_sampleCount, stencilFormat, m_width, m_height);
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::STENCIL_ATTACHMENT, GL::RENDERBUFFER, m_stencilBuffer);
+            }
+            if (m_attributes.depth) {
+                gl.bindRenderbuffer(GL::RENDERBUFFER, m_depthStencilBuffer);
+                extensions.renderbufferStorageMultisampleANGLE(GL::RENDERBUFFER, m_sampleCount, depthFormat, m_width, m_height);
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::DEPTH_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+            }
+        }
+        return true;
+    }
+#endif // USE(OPENGL_ES)
+
+    if (m_attributes.antialias && m_context.isWebGL2()) {
+        // Use an extra FBO for multisample if multisampled_render_to_texture is not supported.
+        m_resolvedFBO = gl.createFramebuffer();
+        m_multisampleColorBuffer = gl.createRenderbuffer();
+        gl.bindFramebuffer(GL::FRAMEBUFFER, m_framebuffer->object());
+        gl.bindRenderbuffer(GL::RENDERBUFFER, m_multisampleColorBuffer);
+        gl.renderbufferStorageMultisample(GL::RENDERBUFFER, m_sampleCount, colorFormat, m_width, m_height);
+        gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::COLOR_ATTACHMENT0, GL::RENDERBUFFER, m_multisampleColorBuffer);
+        if (useDepthStencil) {
+            m_depthStencilBuffer = gl.createRenderbuffer();
+            if (supportsPackedDepthStencil) {
+                gl.bindRenderbuffer(GL::RENDERBUFFER, m_depthStencilBuffer);
+                gl.renderbufferStorageMultisample(GL::RENDERBUFFER, m_sampleCount, depthFormat, m_width, m_height);
+                if (m_attributes.stencil)
+                    gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::STENCIL_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+                if (m_attributes.depth)
+                    gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::DEPTH_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+            } else {
+                if (m_attributes.stencil) {
+                    m_stencilBuffer = gl.createRenderbuffer();
+                    gl.bindRenderbuffer(GL::RENDERBUFFER, m_stencilBuffer);
+                    gl.renderbufferStorageMultisample(GL::RENDERBUFFER, m_sampleCount, stencilFormat, m_width, m_height);
+                    gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::STENCIL_ATTACHMENT, GL::RENDERBUFFER, m_stencilBuffer);
+                }
+                if (m_attributes.depth) {
+                    gl.bindRenderbuffer(GL::RENDERBUFFER, m_depthStencilBuffer);
+                    gl.renderbufferStorageMultisample(GL::RENDERBUFFER, m_sampleCount, depthFormat, m_width, m_height);
+                    gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::DEPTH_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+                }
+            }
+        }
+        return gl.checkFramebufferStatus(GL::FRAMEBUFFER) == GL::FRAMEBUFFER_COMPLETE;
+    }
+    if (useDepthStencil) {
+        gl.bindFramebuffer(GL::FRAMEBUFFER, m_framebuffer->object());
+        m_depthStencilBuffer = gl.createRenderbuffer();
+        if (supportsPackedDepthStencil) {
+            gl.bindRenderbuffer(GL::RENDERBUFFER, m_depthStencilBuffer);
+            gl.renderbufferStorage(GL::RENDERBUFFER, depthFormat, m_width, m_height);
+            if (m_attributes.stencil)
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::STENCIL_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+            if (m_attributes.depth)
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::DEPTH_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+        } else {
+            if (m_attributes.stencil) {
+                m_stencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(GL::RENDERBUFFER, m_stencilBuffer);
+                gl.renderbufferStorage(GL::RENDERBUFFER, stencilFormat, m_width, m_height);
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::STENCIL_ATTACHMENT, GL::RENDERBUFFER, m_stencilBuffer);
+            }
+            if (m_attributes.depth) {
+                gl.bindRenderbuffer(GL::RENDERBUFFER, m_depthStencilBuffer);
+                gl.renderbufferStorage(GL::RENDERBUFFER, depthFormat, m_width, m_height);
+                gl.framebufferRenderbuffer(GL::FRAMEBUFFER, GL::DEPTH_ATTACHMENT, GL::RENDERBUFFER, m_depthStencilBuffer);
+            }
+        }
+    }
+
+    return true;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(WEBXR)

Modified: trunk/Source/WebCore/Modules/webxr/WebXRWebGLLayer.h (274694 => 274695)


--- trunk/Source/WebCore/Modules/webxr/WebXRWebGLLayer.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Modules/webxr/WebXRWebGLLayer.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -30,6 +30,8 @@
 #include "CanvasBase.h"
 #include "ExceptionOr.h"
 #include "FloatRect.h"
+#include "GraphicsTypesGL.h"
+#include "PlatformXR.h"
 #include "WebXRLayer.h"
 #include <wtf/IsoMalloc.h>
 #include <wtf/Optional.h>
@@ -43,9 +45,11 @@
 class IntSize;
 class WebGLFramebuffer;
 class WebGLRenderingContext;
+class WebGLRenderingContextBase;
 #if ENABLE(WEBGL2)
 class WebGL2RenderingContext;
 #endif
+class WebXROpaqueFramebuffer;
 class WebXRSession;
 class WebXRView;
 class WebXRViewport;
@@ -68,7 +72,7 @@
     bool antialias() const;
     bool ignoreDepthValues() const;
 
-    WebGLFramebuffer* framebuffer() const;
+    const WebGLFramebuffer* framebuffer() const;
     unsigned framebufferWidth() const;
     unsigned framebufferHeight() const;
 
@@ -82,8 +86,12 @@
 
     HTMLCanvasElement* canvas() const;
 
+    // WebXRLayer
+    void startFrame(const PlatformXR::Device::FrameData&) final;
+    PlatformXR::Device::Layer endFrame() final;
+
 private:
-    WebXRWebGLLayer(Ref<WebXRSession>&&, WebXRRenderingContext&&, const XRWebGLLayerInit&);
+    WebXRWebGLLayer(Ref<WebXRSession>&&, WebXRRenderingContext&&, std::unique_ptr<WebXROpaqueFramebuffer>&&, bool antialias, bool ignoreDepthValues, bool isCompositionEnabled);
 
     void computeViewports();
     static IntSize computeNativeWebGLFramebufferResolution();
@@ -92,7 +100,6 @@
     void canvasChanged(CanvasBase&, const Optional<FloatRect>&) final { };
     void canvasResized(CanvasBase&) final;
     void canvasDestroyed(CanvasBase&) final { };
-
     Ref<WebXRSession> m_session;
     WebXRRenderingContext m_context;
 
@@ -103,16 +110,50 @@
 
     ViewportData m_leftViewportData;
     ViewportData m_rightViewportData;
+    std::unique_ptr<WebXROpaqueFramebuffer> m_framebuffer;
     bool m_antialias { false };
     bool m_ignoreDepthValues { false };
     bool m_isCompositionEnabled { true };
     bool m_viewportsDirty { true };
+};
 
-    struct {
-        RefPtr<WebGLFramebuffer> object;
-        unsigned width { 0 };
-        unsigned height { 0 };
-    } m_framebuffer;
+class WebXROpaqueFramebuffer {
+public:
+    struct Attributes {
+        bool alpha { true };
+        bool antialias { true };
+        bool depth { true };
+        bool stencil { false };
+    };
+
+    static std::unique_ptr<WebXROpaqueFramebuffer> create(PlatformXR::LayerHandle, WebGLRenderingContextBase&, Attributes&&, uint32_t width, uint32_t height);
+    ~WebXROpaqueFramebuffer();
+
+    PlatformXR::LayerHandle handle() const { return m_handle; }
+    const WebGLFramebuffer& framebuffer() const { return m_framebuffer.get(); }
+    uint32_t width() const { return m_width; }
+    uint32_t height() const { return m_height; }
+
+    void startFrame(const PlatformXR::Device::FrameData::LayerData&);
+    void endFrame();
+
+private:
+    WebXROpaqueFramebuffer(PlatformXR::LayerHandle, Ref<WebGLFramebuffer>&&, WebGLRenderingContextBase&, Attributes&&, uint32_t width, uint32_t height);
+
+    bool setupFramebuffer();
+
+    PlatformXR::LayerHandle m_handle;
+    Ref<WebGLFramebuffer> m_framebuffer;
+    WebGLRenderingContextBase& m_context;
+    Attributes m_attributes;
+    uint32_t m_width { 0 };
+    uint32_t m_height { 0 };
+    PlatformGLObject m_depthStencilBuffer { 0 };
+    PlatformGLObject m_stencilBuffer { 0 };
+    PlatformGLObject m_multisampleColorBuffer { 0 };
+    PlatformGLObject m_resolvedFBO { 0 };
+    GCGLint m_sampleCount { 0 };
+    PlatformGLObject m_opaqueTexture { 0 };
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Sources.txt (274694 => 274695)


--- trunk/Source/WebCore/Sources.txt	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/Sources.txt	2021-03-19 01:27:38 UTC (rev 274695)
@@ -2182,6 +2182,8 @@
 platform/text/TextEncodingRegistry.cpp
 platform/xr/openxr/OpenXRInstance.cpp @no-unify
 platform/xr/openxr/OpenXRExtensions.cpp @no-unify
+platform/xr/openxr/OpenXRLayer.cpp @no-unify
+platform/xr/openxr/OpenXRSwapchain.cpp @no-unify
 platform/xr/openxr/PlatformXROpenXR.cpp @no-unify
 plugins/DOMMimeType.cpp
 plugins/DOMMimeTypeArray.cpp

Modified: trunk/Source/WebCore/html/canvas/WebGL2RenderingContext.cpp (274694 => 274695)


--- trunk/Source/WebCore/html/canvas/WebGL2RenderingContext.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/html/canvas/WebGL2RenderingContext.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -2815,6 +2815,14 @@
             return m_context->getFramebufferAttachmentParameteri(target, attachment, pname);
         }
     }
+
+#if ENABLE(WEBXR)
+    if (targetFramebuffer->isOpaque()) {
+        synthesizeGLError(GraphicsContextGL::INVALID_OPERATION, "getFramebufferAttachmentParameter", "An opaque framebuffer's attachments cannot be inspected or changed");
+        return nullptr;
+    }
+#endif
+
     if (!validateNonDefaultFramebufferAttachment(functionName, attachment))
         return nullptr;
 

Modified: trunk/Source/WebCore/html/canvas/WebGLFramebuffer.cpp (274694 => 274695)


--- trunk/Source/WebCore/html/canvas/WebGLFramebuffer.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/html/canvas/WebGLFramebuffer.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -309,6 +309,17 @@
     return adoptRef(*new WebGLFramebuffer(ctx));
 }
 
+#if ENABLE(WEBXR)
+
+Ref<WebGLFramebuffer> WebGLFramebuffer::createOpaque(WebGLRenderingContextBase& ctx)
+{
+    auto framebuffer = adoptRef(*new WebGLFramebuffer(ctx));
+    framebuffer->m_opaque = true;
+    return framebuffer;
+}
+
+#endif
+
 WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContextBase& ctx)
     : WebGLContextObject(ctx)
     , m_hasEverBeenBound(false)
@@ -489,6 +500,16 @@
 
 GCGLenum WebGLFramebuffer::checkStatus(const char** reason) const
 {
+#if ENABLE(WEBXR)
+    // https://immersive-web.github.io/webxr/#opaque-framebuffer
+    if (m_opaque) {
+        if (m_opaqueActive)
+            return GL_FRAMEBUFFER_COMPLETE;
+        *reason = "An opaque framebuffer is considered incomplete outside of a requestAnimationFrame";
+        return GL_FRAMEBUFFER_UNSUPPORTED;
+    }
+#endif
+
     unsigned int count = 0;
     GCGLsizei width = 0, height = 0;
     bool haveDepth = false;

Modified: trunk/Source/WebCore/html/canvas/WebGLFramebuffer.h (274694 => 274695)


--- trunk/Source/WebCore/html/canvas/WebGLFramebuffer.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/html/canvas/WebGLFramebuffer.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -74,6 +74,9 @@
     virtual ~WebGLFramebuffer();
 
     static Ref<WebGLFramebuffer> create(WebGLRenderingContextBase&);
+#if ENABLE(WEBXR)
+    static Ref<WebGLFramebuffer> createOpaque(WebGLRenderingContextBase&);
+#endif
 
     void setAttachmentForBoundFramebuffer(GCGLenum target, GCGLenum attachment, GCGLenum texTarget, WebGLTexture*, GCGLint level, GCGLint layer);
     void setAttachmentForBoundFramebuffer(GCGLenum target, GCGLenum attachment, WebGLRenderbuffer*);
@@ -116,6 +119,11 @@
 
     void addMembersToOpaqueRoots(const WTF::AbstractLocker&, JSC::AbstractSlotVisitor&);
 
+#if ENABLE(WEBXR)
+    bool isOpaque() const { return m_opaque; }
+    void setOpaqueActive(bool active) { m_opaqueActive = active; }
+#endif
+
 private:
     WebGLFramebuffer(WebGLRenderingContextBase&);
 
@@ -151,6 +159,11 @@
 
     Vector<GCGLenum> m_drawBuffers;
     Vector<GCGLenum> m_filteredDrawBuffers;
+
+#if ENABLE(WEBXR)
+    bool m_opaque { false };
+    bool m_opaqueActive { false };
+#endif
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp (274694 => 274695)


--- trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -305,6 +305,13 @@
         return nullptr;
     }
 
+#if ENABLE(WEBXR)
+    if (m_framebufferBinding->isOpaque()) {
+        synthesizeGLError(GraphicsContextGL::INVALID_OPERATION, "getFramebufferAttachmentParameter", "An opaque framebuffer's attachments cannot be inspected or changed");
+        return nullptr;
+    }
+#endif
+
     auto object = makeRefPtr(m_framebufferBinding->getAttachmentObject(attachment));
     if (!object) {
         if (pname == GraphicsContextGL::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)

Modified: trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp (274694 => 274695)


--- trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -2054,6 +2054,13 @@
 {
     auto locker = holdLock(objectGraphLock());
 
+#if ENABLE(WEBXR)
+    if (framebuffer && framebuffer->isOpaque()) {
+        synthesizeGLError(GraphicsContextGL::INVALID_OPERATION, "deleteFramebuffer", "An opaque framebuffer's attachments cannot be inspected or changed");
+        return;
+    }
+#endif
+
     if (!deleteObject(locker, framebuffer))
         return;
 
@@ -2734,6 +2741,14 @@
         synthesizeGLError(GraphicsContextGL::INVALID_OPERATION, "framebufferRenderbuffer", "no framebuffer bound");
         return;
     }
+
+#if ENABLE(WEBXR)
+    if (framebufferBinding->isOpaque()) {
+        synthesizeGLError(GraphicsContextGL::INVALID_OPERATION, "framebufferRenderbuffer", "An opaque framebuffer's attachments cannot be inspected or changed");
+        return;
+    }
+#endif
+
     framebufferBinding->setAttachmentForBoundFramebuffer(target, attachment, buffer);
     applyStencilTest();
 }
@@ -2757,6 +2772,13 @@
         synthesizeGLError(GraphicsContextGL::INVALID_OPERATION, "framebufferTexture2D", "no framebuffer bound");
         return;
     }
+#if ENABLE(WEBXR)
+    if (framebufferBinding->isOpaque()) {
+        synthesizeGLError(GraphicsContextGL::INVALID_OPERATION, "framebufferTexture2D", "An opaque framebuffer's attachments cannot be inspected or changed");
+        return;
+    }
+#endif
+
     framebufferBinding->setAttachmentForBoundFramebuffer(target, attachment, textarget, texture, level, 0);
     applyStencilTest();
 }

Modified: trunk/Source/WebCore/platform/xr/PlatformXR.h (274694 => 274695)


--- trunk/Source/WebCore/platform/xr/PlatformXR.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/platform/xr/PlatformXR.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -19,6 +19,8 @@
 #pragma once
 
 #include "FloatPoint3D.h"
+#include "GraphicsTypesGL.h"
+#include "IntRect.h"
 #include "IntSize.h"
 #include <memory>
 #include <wtf/CompletionHandler.h>
@@ -51,6 +53,8 @@
     Right,
 };
 
+using LayerHandle = int;
+
 #if ENABLE(WEBXR)
 
 class TrackingAndRenderingClient : public CanMakeWeakPtr<TrackingAndRenderingClient> {
@@ -80,6 +84,15 @@
     bool supportsOrientationTracking() const { return m_supportsOrientationTracking; }
     bool supportsViewportScaling() const { return m_supportsViewportScaling; }
 
+    // Returns the value that the device's recommended resolution must be multiplied by
+    // to yield the device's native framebuffer resolution.
+    virtual double nativeFramebufferScalingFactor() const { return 1.0; }
+    // Returns the value that the device's recommended resolution must be multiplied by
+    // to yield the device's max framebuffer resolution. This resolution can be larger than
+    // the native resolution if the device supports supersampling.
+    virtual double maxFramebufferScalingFactor() const { return nativeFramebufferScalingFactor(); }
+
+
     virtual void initializeTrackingAndRendering(SessionMode) = 0;
     virtual void shutDownTrackingAndRendering() = 0;
     void setTrackingAndRenderingClient(WeakPtr<TrackingAndRenderingClient>&& client) { m_trackingAndRenderingClient = WTFMove(client); }
@@ -88,6 +101,8 @@
     // when the platform has completed all steps to shut down the XR session.
     virtual bool supportsSessionShutdownNotification() const { return false; }
     virtual void initializeReferenceSpace(ReferenceSpaceType) = 0;
+    virtual Optional<LayerHandle> createLayerProjection(uint32_t width, uint32_t height, bool alpha) = 0;
+    virtual void deleteLayer(LayerHandle) = 0;
 
     struct FrameData {
         struct FloatQuaternion {
@@ -122,16 +137,33 @@
             Vector<WebCore::FloatPoint> bounds;
         };
 
+        struct LayerData {
+            PlatformGLObject opaqueTexture { 0 };
+        };
+
         bool isTrackingValid { false };
         bool isPositionValid { false };
         bool isPositionEmulated { false };
+        bool shouldRender { false };
         long predictedDisplayTime { 0 };
         Pose origin;
         Optional<Pose> floorTransform;
         StageParameters stageParameters;
         Vector<View> views;
+        HashMap<LayerHandle, LayerData> layers;
     };
 
+    struct LayerView {
+        Eye eye { Eye::None };
+        WebCore::IntRect viewport;
+    };
+
+    struct Layer {
+        LayerHandle handle { 0 };
+        bool visible { true };
+        Vector<LayerView> views;
+    };
+
     struct ViewData {
         bool active { false };
         Eye eye { Eye::None };
@@ -141,6 +173,7 @@
 
     using RequestFrameCallback = Function<void(FrameData&&)>;
     virtual void requestFrame(RequestFrameCallback&&) = 0;
+    virtual void submitFrame(Vector<Layer>&&) { };
 protected:
     Device() = default;
 

Modified: trunk/Source/WebCore/platform/xr/cocoa/PlatformXRCocoa.h (274694 => 274695)


--- trunk/Source/WebCore/platform/xr/cocoa/PlatformXRCocoa.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/platform/xr/cocoa/PlatformXRCocoa.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -37,6 +37,8 @@
     void initializeReferenceSpace(ReferenceSpaceType) override { }
     Vector<ViewData> views(SessionMode) const override { return { }; }
     void requestFrame(RequestFrameCallback&&) override { }
+    Optional<LayerHandle> createLayerProjection(uint32_t, uint32_t, bool) override { return WTF::nullopt; };
+    void deleteLayer(LayerHandle) override { };
 };
 
 }

Added: trunk/Source/WebCore/platform/xr/openxr/OpenXRLayer.cpp (0 => 274695)


--- trunk/Source/WebCore/platform/xr/openxr/OpenXRLayer.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/xr/openxr/OpenXRLayer.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "OpenXRLayer.h"
+
+#if ENABLE(WEBXR) && USE(OPENXR)
+
+using namespace WebCore;
+
+namespace PlatformXR {
+
+// OpenXRLayerProjection
+
+std::unique_ptr<OpenXRLayerProjection> OpenXRLayerProjection::create(XrInstance instance, XrSession session, uint32_t width, uint32_t height, int64_t format, uint32_t sampleCount)
+{
+    if (!width || !height || !sampleCount)
+        return nullptr;
+
+    auto info = createStructure<XrSwapchainCreateInfo, XR_TYPE_SWAPCHAIN_CREATE_INFO>();
+    info.arraySize = 1;
+    info.format = format;
+    info.width = width;
+    info.height = height;
+    info.mipCount = 1;
+    info.faceCount = 1;
+    info.arraySize = 1;
+    info.sampleCount = sampleCount;
+    info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
+
+    auto swapchain = OpenXRSwapchain::create(instance, session, info);
+    if (!swapchain)
+        return nullptr;
+
+    return std::unique_ptr<OpenXRLayerProjection>(new OpenXRLayerProjection(makeUniqueRefFromNonNullUniquePtr(WTFMove(swapchain))));
+}
+
+OpenXRLayerProjection::OpenXRLayerProjection(UniqueRef<OpenXRSwapchain>&& swapchain)
+    : m_swapchain(WTFMove(swapchain))
+    , m_layerProjection(createStructure<XrCompositionLayerProjection, XR_TYPE_COMPOSITION_LAYER_PROJECTION>())
+{
+}
+
+Optional<Device::FrameData::LayerData> OpenXRLayerProjection::startFrame()
+{
+    auto texture = m_swapchain->acquireImage();
+    if (!texture)
+        return WTF::nullopt;
+
+    return Device::FrameData::LayerData { *texture };
+}
+
+XrCompositionLayerBaseHeader* OpenXRLayerProjection::endFrame(const Device::Layer& layer, XrSpace space, const Vector<XrView>& frameViews)
+{
+    ASSERT(layer.views.size() == frameViews.size());
+
+    auto viewCount = frameViews.size();
+    m_projectionViews.fill(createStructure<XrCompositionLayerProjectionView, XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW>(), viewCount);
+    m_layerProjection.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
+    for (uint32_t i = 0; i < viewCount; ++i) {
+        m_projectionViews[i].pose = frameViews[i].pose;
+        m_projectionViews[i].fov = frameViews[i].fov;
+        m_projectionViews[i].subImage.swapchain = m_swapchain->swapchain();
+
+        auto& viewport = layer.views[i].viewport;
+
+        m_projectionViews[i].subImage.imageRect.offset = { viewport.x(), viewport.y() };
+        m_projectionViews[i].subImage.imageRect.extent = { viewport.width(), viewport.height() };
+    }
+
+    m_layerProjection.space = space;
+    m_layerProjection.viewCount = m_projectionViews.size();
+    m_layerProjection.views = m_projectionViews.data();
+
+    m_swapchain->releaseImage();
+
+    return reinterpret_cast<XrCompositionLayerBaseHeader*>(&m_layerProjection);
+}
+
+
+} // namespace PlatformXR
+
+#endif // ENABLE(WEBXR) && USE(OPENXR)

Added: trunk/Source/WebCore/platform/xr/openxr/OpenXRLayer.h (0 => 274695)


--- trunk/Source/WebCore/platform/xr/openxr/OpenXRLayer.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/xr/openxr/OpenXRLayer.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(WEBXR) && USE(OPENXR)
+
+#include "OpenXRSwapchain.h"
+#include "OpenXRUtils.h"
+#include "PlatformXR.h"
+
+#include <wtf/Noncopyable.h>
+#include <wtf/UniqueRef.h>
+#include <wtf/Vector.h>
+
+namespace PlatformXR {
+
+class OpenXRLayer {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(OpenXRLayer);
+public:
+    virtual ~OpenXRLayer() = default;
+
+    virtual Optional<Device::FrameData::LayerData> startFrame() = 0;
+    virtual XrCompositionLayerBaseHeader* endFrame(const Device::Layer&, XrSpace, const Vector<XrView>&) = 0;
+
+protected:
+    OpenXRLayer() = default;
+};
+
+class OpenXRLayerProjection final: public OpenXRLayer  {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(OpenXRLayerProjection);
+public:
+    static std::unique_ptr<OpenXRLayerProjection> create(XrInstance, XrSession, uint32_t width, uint32_t height, int64_t format, uint32_t sampleCount);
+private:
+    OpenXRLayerProjection(UniqueRef<OpenXRSwapchain>&&);
+
+    Optional<Device::FrameData::LayerData> startFrame() final;
+    XrCompositionLayerBaseHeader* endFrame(const Device::Layer&, XrSpace, const Vector<XrView>&) final;
+
+    UniqueRef<OpenXRSwapchain> m_swapchain;
+    XrCompositionLayerProjection m_layerProjection;
+    Vector<XrCompositionLayerProjectionView> m_projectionViews;
+};
+
+} // namespace PlatformXR
+
+#endif // ENABLE(WEBXR) && USE(OPENXR)

Added: trunk/Source/WebCore/platform/xr/openxr/OpenXRSwapchain.cpp (0 => 274695)


--- trunk/Source/WebCore/platform/xr/openxr/OpenXRSwapchain.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/xr/openxr/OpenXRSwapchain.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEBXR) && USE(OPENXR)
+#include "OpenXRSwapchain.h"
+
+using namespace WebCore;
+
+namespace PlatformXR {
+
+std::unique_ptr<OpenXRSwapchain> OpenXRSwapchain::create(XrInstance instance, XrSession session, const XrSwapchainCreateInfo& info)
+{
+    ASSERT(session != XR_NULL_HANDLE);
+    ASSERT(info.faceCount == 1);
+
+    XrSwapchain swapchain { XR_NULL_HANDLE };
+    auto result = xrCreateSwapchain(session, &info, &swapchain);
+    RETURN_IF_FAILED(result, "xrEnumerateInstanceExtensionProperties", instance, nullptr);
+    ASSERT(swapchain != XR_NULL_HANDLE);
+
+    uint32_t imageCount;
+    result = xrEnumerateSwapchainImages(swapchain, 0, &imageCount, nullptr);
+    RETURN_IF_FAILED(result, "xrEnumerateSwapchainImages", instance, nullptr);
+    if (!imageCount) {
+        LOG(XR, "xrEnumerateSwapchainImages(): no images\n");
+        return nullptr;
+    }
+
+    Vector<XrSwapchainImageOpenGLKHR> imageBuffers(imageCount, [] {
+        return createStructure<XrSwapchainImageOpenGLKHR, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR>();
+    }());
+
+    Vector<XrSwapchainImageBaseHeader*> imageHeaders = imageBuffers.map([](auto& image) mutable {
+        return (XrSwapchainImageBaseHeader*) &image;
+    });
+
+    // Get images from an XrSwapchain
+    result = xrEnumerateSwapchainImages(swapchain, imageCount, &imageCount, imageHeaders[0]);
+    RETURN_IF_FAILED(result, "xrEnumerateSwapchainImages with imageCount", instance, nullptr);
+
+    return std::unique_ptr<OpenXRSwapchain>(new OpenXRSwapchain(instance, session, swapchain, info, WTFMove(imageBuffers)));
+}
+
+OpenXRSwapchain::OpenXRSwapchain(XrInstance instance, XrSession session, XrSwapchain swapchain, const XrSwapchainCreateInfo& info, Vector<XrSwapchainImageOpenGLKHR>&& imageBuffers)
+    : m_instance(instance)
+    , m_session(session)
+    , m_swapchain(swapchain)
+    , m_createInfo(info)
+    , m_imageBuffers(WTFMove(imageBuffers))
+{
+}
+
+OpenXRSwapchain::~OpenXRSwapchain()
+{
+    if (m_acquiredTexture)
+        releaseImage();
+    if (m_swapchain != XR_NULL_HANDLE)
+        xrDestroySwapchain(m_swapchain);
+}
+
+Optional<PlatformGLObject> OpenXRSwapchain::acquireImage()
+{
+    RELEASE_ASSERT_WITH_MESSAGE(!m_acquiredTexture , "Expected no acquired images. ReleaseImage not called?");
+
+    auto acquireInfo = createStructure<XrSwapchainImageAcquireInfo, XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO>();
+    uint32_t swapchainImageIndex = 0;
+    auto result = xrAcquireSwapchainImage(m_swapchain, &acquireInfo, &swapchainImageIndex);
+    RETURN_IF_FAILED(result, "xrAcquireSwapchainImage", m_instance, WTF::nullopt);
+
+    RELEASE_ASSERT(swapchainImageIndex < m_imageBuffers.size());
+
+    auto waitInfo = createStructure<XrSwapchainImageWaitInfo, XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO>();
+    waitInfo.timeout = XR_INFINITE_DURATION;
+    result = xrWaitSwapchainImage(m_swapchain, &waitInfo);
+    RETURN_IF_FAILED(result, "xrWaitSwapchainImage", m_instance, WTF::nullopt);
+
+    m_acquiredTexture = m_imageBuffers[swapchainImageIndex].image;
+
+    return m_acquiredTexture;
+}
+
+void OpenXRSwapchain::releaseImage()
+{
+    RELEASE_ASSERT_WITH_MESSAGE(m_acquiredTexture, "Expected a valid acquired image. AcquireImage not called?");
+
+    auto releaseInfo = createStructure<XrSwapchainImageReleaseInfo, XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO>();
+    auto result = xrReleaseSwapchainImage(m_swapchain, &releaseInfo);
+    LOG_IF_FAILED(result, "xrReleaseSwapchainImage", m_instance);
+
+    m_acquiredTexture = 0;
+}
+
+} // namespace PlatformXR
+
+#endif // ENABLE(WEBXR) && USE(OPENXR)

Added: trunk/Source/WebCore/platform/xr/openxr/OpenXRSwapchain.h (0 => 274695)


--- trunk/Source/WebCore/platform/xr/openxr/OpenXRSwapchain.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/xr/openxr/OpenXRSwapchain.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(WEBXR) && USE(OPENXR)
+
+#include "GraphicsContextGL.h"
+#include "OpenXRUtils.h"
+
+#include <wtf/Noncopyable.h>
+#include <wtf/Vector.h>
+
+namespace PlatformXR {
+
+class OpenXRSwapchain {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(OpenXRSwapchain);
+public:
+    static std::unique_ptr<OpenXRSwapchain> create(XrInstance, XrSession, const XrSwapchainCreateInfo&);
+    ~OpenXRSwapchain();
+
+    Optional<PlatformGLObject> acquireImage();
+    void releaseImage();
+    XrSwapchain swapchain() const { return m_swapchain; }
+    int32_t width() const { return m_createInfo.width; }
+    int32_t height() const { return m_createInfo.height; }
+
+private:
+    OpenXRSwapchain(XrInstance, XrSession, XrSwapchain, const XrSwapchainCreateInfo&, Vector<XrSwapchainImageOpenGLKHR>&&);
+
+    XrInstance m_instance;
+    XrSession m_session;
+    XrSwapchain m_swapchain;
+    XrSwapchainCreateInfo m_createInfo;
+    Vector<XrSwapchainImageOpenGLKHR> m_imageBuffers;
+    PlatformGLObject m_acquiredTexture { 0 };
+};
+
+
+} // namespace PlatformXR
+
+#endif // ENABLE(WEBXR) && USE(OPENXR)

Modified: trunk/Source/WebCore/platform/xr/openxr/OpenXRUtils.h (274694 => 274695)


--- trunk/Source/WebCore/platform/xr/openxr/OpenXRUtils.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/platform/xr/openxr/OpenXRUtils.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -70,6 +70,11 @@
         return __VA_ARGS__;                                                                     \
     }
 
+#define LOG_IF_FAILED(result, call, instance, ...)                                           \
+    if (XR_FAILED(result))                                                                   \
+        LOG(XR, "%s %s: %s\n", __func__, call, resultToString(result, instance).utf8().data());
+
+
 inline Device::FrameData::Pose XrPosefToPose(XrPosef pose)
 {
     Device::FrameData::Pose result;
@@ -81,7 +86,7 @@
 inline Device::FrameData::View xrViewToPose(XrView view)
 {
     Device::FrameData::View pose;
-    pose.projection = Device::FrameData::Fov { view.fov.angleUp, -view.fov.angleDown, -view.fov.angleLeft, view.fov.angleRight };
+    pose.projection = Device::FrameData::Fov { fabs(view.fov.angleUp), fabs(view.fov.angleDown), fabs(view.fov.angleLeft), fabs(view.fov.angleRight) };
     pose.offset = XrPosefToPose(view.pose);
     return pose;
 }

Modified: trunk/Source/WebCore/platform/xr/openxr/PlatformXROpenXR.cpp (274694 => 274695)


--- trunk/Source/WebCore/platform/xr/openxr/PlatformXROpenXR.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/platform/xr/openxr/PlatformXROpenXR.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -26,17 +26,12 @@
 
 #include <wtf/NeverDestroyed.h>
 #include <wtf/Optional.h>
+#include <wtf/threads/BinarySemaphore.h>
 
 using namespace WebCore;
 
 namespace PlatformXR {
 
-
-static bool isSessionActive(XrSessionState state)
-{
-    return state == XR_SESSION_STATE_VISIBLE || state == XR_SESSION_STATE_FOCUSED;
-}
-
 static bool isSessionReady(XrSessionState state)
 {
     return state >= XR_SESSION_STATE_READY  && state < XR_SESSION_STATE_STOPPING;
@@ -52,7 +47,7 @@
 OpenXRDevice::OpenXRDevice(XrInstance instance, XrSystemId system, Ref<WorkQueue>&& queue, const OpenXRExtensions& extensions)
     : m_instance(instance)
     , m_systemId(system)
-    , m_queue(queue)
+    , m_queue(WTFMove(queue))
     , m_extensions(extensions)
 {
 }
@@ -113,6 +108,17 @@
         auto& context = static_cast<GLContext&>(*m_egl);
         context.makeContextCurrent();
 
+        GraphicsContextGLAttributes attributes;
+        attributes.depth = false;
+        attributes.stencil = false;
+        attributes.antialias = false;
+
+        m_gl = GraphicsContextGL::create(attributes, nullptr);
+        if (!m_gl) {
+            LOG(XR, "Failed to create a valid GraphicsContextGL");
+            return;
+        }
+
         m_graphicsBinding.display = PlatformDisplay::sharedDisplay().eglDisplay();
         m_graphicsBinding.context = context.platformContext();
         m_graphicsBinding.config = m_egl->config();
@@ -124,7 +130,7 @@
         sessionCreateInfo.next = &m_graphicsBinding;
 
         result = xrCreateSession(m_instance, &sessionCreateInfo, &m_session);
-        RETURN_IF_FAILED(result, "xrEnumerateInstanceExtensionProperties", m_instance);
+        RETURN_IF_FAILED(result, "xrCreateSession", m_instance);
 
         // Create the default reference spaces
         m_localSpace = createReferenceSpace(XR_REFERENCE_SPACE_TYPE_LOCAL);
@@ -170,11 +176,10 @@
             return;
         }
 
-        auto frameState = createStructure<XrFrameState, XR_TYPE_FRAME_STATE>();
+        m_frameState = createStructure<XrFrameState, XR_TYPE_FRAME_STATE>();
         auto frameWaitInfo = createStructure<XrFrameWaitInfo, XR_TYPE_FRAME_WAIT_INFO>();
-        auto result = xrWaitFrame(m_session, &frameWaitInfo, &frameState);
+        auto result = xrWaitFrame(m_session, &frameWaitInfo, &m_frameState);
         RETURN_IF_FAILED(result, "xrWaitFrame", m_instance);
-        XrTime predictedTime = frameState.predictedDisplayTime;
 
         auto frameBeginInfo = createStructure<XrFrameBeginInfo, XR_TYPE_FRAME_BEGIN_INFO>();
         result = xrBeginFrame(m_session, &frameBeginInfo);
@@ -181,7 +186,8 @@
         RETURN_IF_FAILED(result, "xrBeginFrame", m_instance);
 
         Device::FrameData frameData;
-        frameData.predictedDisplayTime = frameState.predictedDisplayTime;
+        frameData.predictedDisplayTime = m_frameState.predictedDisplayTime;
+        frameData.shouldRender = m_frameState.shouldRender;
         frameData.stageParameters = m_stageParameters;
 
         ASSERT(m_configurationViews.contains(m_currentViewConfigurationType));
@@ -188,15 +194,12 @@
         const auto& configurationView = m_configurationViews.get(m_currentViewConfigurationType);
 
         uint32_t viewCount = configurationView.size();
-        Vector<XrView> views(viewCount, [] {
-            return createStructure<XrView, XR_TYPE_VIEW>();
-        }());
+        m_frameViews.fill(createStructure<XrView, XR_TYPE_VIEW>(), viewCount);
 
-
-        if (isSessionActive(m_sessionState)) {
+        if (m_frameState.shouldRender) {
             // Query head location
             auto location = createStructure<XrSpaceLocation, XR_TYPE_SPACE_LOCATION>();
-            xrLocateSpace(m_viewSpace, m_localSpace, frameState.predictedDisplayTime, &location);
+            xrLocateSpace(m_viewSpace, m_localSpace, m_frameState.predictedDisplayTime, &location);
             frameData.isTrackingValid = location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT;
             frameData.isPositionValid = location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT;
             frameData.isPositionEmulated = location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT;
@@ -203,17 +206,16 @@
 
             if (frameData.isTrackingValid)
                 frameData.origin = XrPosefToPose(location.pose);
-            
 
             auto viewLocateInfo = createStructure<XrViewLocateInfo, XR_TYPE_VIEW_LOCATE_INFO>();
-            viewLocateInfo.displayTime = predictedTime;
-            viewLocateInfo.space = m_localSpace;
+            viewLocateInfo.displayTime = m_frameState.predictedDisplayTime;
+            viewLocateInfo.space = m_viewSpace;
 
             auto viewState = createStructure<XrViewState, XR_TYPE_VIEW_STATE>();
             uint32_t viewCountOutput;
-            result = xrLocateViews(m_session, &viewLocateInfo, &viewState, viewCount, &viewCountOutput, views.data());
+            result = xrLocateViews(m_session, &viewLocateInfo, &viewState, viewCount, &viewCountOutput, m_frameViews.data());
             if (!XR_FAILED(result)) {
-                for (auto& view : views)
+                for (auto& view : m_frameViews)
                     frameData.views.append(xrViewToPose(view));
             }
 
@@ -220,23 +222,62 @@
             // Query floor transform
             if (m_stageSpace != XR_NULL_HANDLE) {
                 auto floorLocation = createStructure<XrSpaceLocation, XR_TYPE_SPACE_LOCATION>();
-                xrLocateSpace(m_stageSpace, m_localSpace, frameState.predictedDisplayTime, &floorLocation);
+                xrLocateSpace(m_stageSpace, m_localSpace, m_frameState.predictedDisplayTime, &floorLocation);
                 frameData.floorTransform = { XrPosefToPose(floorLocation.pose) };
             }
+
+            for (auto& layer : m_layers) {
+                auto layerData = layer.value->startFrame();
+                if (layerData)
+                    frameData.layers.add(layer.key, *layerData);
+            }
+        } else {
+            // https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/XrFrameState.html
+            // When shouldRender is false the application should avoid heavy GPU work where possible,
+            // for example by skipping layer rendering and then omitting those layers when calling xrEndFrame.
+            // In this case we don't need to wait for OpenXRDevice::submitFrame to finish the frame.
+            auto frameEndInfo = createStructure<XrFrameEndInfo, XR_TYPE_FRAME_END_INFO>();
+            frameEndInfo.displayTime = m_frameState.predictedDisplayTime;
+            frameEndInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+            frameEndInfo.layerCount = 0;
+            frameEndInfo.layers = nullptr;
+            LOG_IF_FAILED(xrEndFrame(m_session, &frameEndInfo), "xrEndFrame", m_instance);
         }
 
         callOnMainThread([frameData = WTFMove(frameData), callback = WTFMove(callback)]() mutable {
             callback(WTFMove(frameData));
         });
+    });
+}
 
+void OpenXRDevice::submitFrame(Vector<Device::Layer>&& layers)
+{   
+    m_queue.dispatch([this, protectedThis = makeRef(*this), layers = WTFMove(layers)]() mutable {
+        ASSERT(m_frameState.shouldRender);
+        Vector<const XrCompositionLayerBaseHeader*> frameEndLayers;
+        if (m_frameState.shouldRender) {
+            for (auto& layer : layers) {
+                auto it = m_layers.find(layer.handle);
+                if (it == m_layers.end()) {
+                    LOG("Didn't find a OpenXRLayer with %d handle", layer.handle);
+                    continue;
+                }
+                auto header = it->value->endFrame(layer, m_viewSpace, m_frameViews);
+                if (!header) {
+                    LOG("endFrame() call failed in OpenXRLayer with %d handle", layer.handle());
+                    continue;
+                }
 
-        Vector<const XrCompositionLayerBaseHeader*> layers;
+                frameEndLayers.append(header);
+            }
+        }
 
         auto frameEndInfo = createStructure<XrFrameEndInfo, XR_TYPE_FRAME_END_INFO>();
-        frameEndInfo.displayTime = predictedTime;
+        frameEndInfo.displayTime = m_frameState.predictedDisplayTime;
         frameEndInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
-        frameEndInfo.layerCount = layers.size();
-        result = xrEndFrame(m_session, &frameEndInfo);
+        frameEndInfo.layerCount = frameEndLayers.size();
+        frameEndInfo.layers = frameEndLayers.data();
+        auto result = xrEndFrame(m_session, &frameEndInfo);
         RETURN_IF_FAILED(result, "xrEndFrame", m_instance);
     });
 }
@@ -256,8 +297,39 @@
     return views;
 }
 
-Device::FeatureList OpenXRDevice::collectSupportedFeatures()
+Optional<LayerHandle> OpenXRDevice::createLayerProjection(uint32_t width, uint32_t height, bool alpha)
 {
+    Optional<LayerHandle> handle;
+
+    BinarySemaphore semaphore;
+    m_queue.dispatch([this, width, height, alpha, &handle, &semaphore]() mutable {
+        auto format = alpha ? GL_RGBA8 : GL_RGB8;
+        int64_t sampleCount = 1;
+
+        auto it = m_configurationViews.find(m_currentViewConfigurationType);
+        if (it != m_configurationViews.end())
+            sampleCount = it->value[0].recommendedSwapchainSampleCount;
+
+        auto layer = OpenXRLayerProjection::create(m_instance, m_session, width, height, format, sampleCount);
+        if (layer) {
+            handle = generateLayerHandle();
+            m_layers.add(*handle, WTFMove(layer));
+        }
+
+        semaphore.signal();
+    });
+    semaphore.wait();
+
+    return handle;
+}
+
+void OpenXRDevice::deleteLayer(LayerHandle handle)
+{
+    m_layers.remove(handle);
+}
+
+Device::FeatureList OpenXRDevice::collectSupportedFeatures() const
+{
     Device::FeatureList features;
 
     // https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/XrReferenceSpaceType.html
@@ -427,6 +499,7 @@
 void OpenXRDevice::resetSession()
 {
     ASSERT(&RunLoop::current() == &m_queue.runLoop());
+    m_layers.clear();
     if (m_session != XR_NULL_HANDLE) {
         xrDestroySession(m_session);
         m_session = XR_NULL_HANDLE;
@@ -438,6 +511,7 @@
     m_stageParameters = { };
 
     // deallocate graphic resources
+    m_gl = nullptr;
     m_egl.reset();
 }
 

Modified: trunk/Source/WebCore/platform/xr/openxr/PlatformXROpenXR.h (274694 => 274695)


--- trunk/Source/WebCore/platform/xr/openxr/PlatformXROpenXR.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/platform/xr/openxr/PlatformXROpenXR.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -22,6 +22,8 @@
 #if ENABLE(WEBXR) && USE(OPENXR)
 
 #include "GLContextEGL.h"
+#include "GraphicsContextGL.h"
+#include "OpenXRLayer.h"
 #include "OpenXRUtils.h"
 #include "PlatformXR.h"
 
@@ -59,10 +61,13 @@
     void initializeReferenceSpace(PlatformXR::ReferenceSpaceType) final;
     bool supportsSessionShutdownNotification() const final { return true; }
     void requestFrame(RequestFrameCallback&&) final;
+    void submitFrame(Vector<Device::Layer>&&) final;
     Vector<ViewData> views(SessionMode) const final;
+    Optional<LayerHandle> createLayerProjection(uint32_t width, uint32_t height, bool alpha) final;
+    void deleteLayer(LayerHandle) final;
 
     // Custom methods
-    FeatureList collectSupportedFeatures();
+    FeatureList collectSupportedFeatures() const;
     void collectSupportedSessionModes();
     void collectConfigurationViews();
     XrSpace createReferenceSpace(XrReferenceSpaceType);
@@ -73,6 +78,7 @@
     void handleSessionStateChange();
     void waitUntilStopping();
     void updateStageParameters();
+    LayerHandle generateLayerHandle() { return ++m_handleIndex; }
 
     XrInstance m_instance;
     XrSystemId m_systemId;
@@ -82,6 +88,11 @@
     XrSessionState m_sessionState { XR_SESSION_STATE_UNKNOWN };
     XrGraphicsBindingEGLMNDX m_graphicsBinding;
     std::unique_ptr<WebCore::GLContextEGL> m_egl;
+    RefPtr<WebCore::GraphicsContextGL> m_gl;
+    XrFrameState m_frameState;
+    Vector<XrView> m_frameViews;
+    HashMap<LayerHandle, std::unique_ptr<OpenXRLayer>> m_layers;
+    LayerHandle m_handleIndex { 0 };
 
     using ViewConfigurationPropertiesMap = HashMap<XrViewConfigurationType, XrViewConfigurationProperties, IntHash<XrViewConfigurationType>, WTF::StrongEnumHashTraits<XrViewConfigurationType>>;
     ViewConfigurationPropertiesMap m_viewConfigurationProperties;

Modified: trunk/Source/WebCore/testing/WebFakeXRDevice.cpp (274694 => 274695)


--- trunk/Source/WebCore/testing/WebFakeXRDevice.cpp	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/testing/WebFakeXRDevice.cpp	2021-03-19 01:27:38 UTC (rev 274695)
@@ -29,6 +29,8 @@
 #if ENABLE(WEBXR)
 
 #include "DOMPointReadOnly.h"
+#include "GLContext.h"
+#include "GraphicsContextGL.h"
 #include "JSDOMPromiseDeferred.h"
 #include "WebFakeXRInputController.h"
 #include <wtf/CompletionHandler.h>
@@ -75,15 +77,29 @@
 
 WebCore::IntSize SimulatedXRDevice::recommendedResolution(PlatformXR::SessionMode)
 {
-    // Return at least a 2 pixel size so we can have different viewports for left and right eyes
-    return IntSize(2, 2);
+    // Return at least a valid size for a framebuffer.
+    return IntSize(32, 32);
 }
 
+void SimulatedXRDevice::initializeTrackingAndRendering(PlatformXR::SessionMode)
+{
+    GraphicsContextGLAttributes attributes;
+    attributes.depth = false;
+    attributes.stencil = false;
+    attributes.antialias = false;
+    m_gl = GraphicsContextGL::create(attributes, nullptr);
+}
+
 void SimulatedXRDevice::shutDownTrackingAndRendering()
 {
     if (m_supportsShutdownNotification)
         simulateShutdownCompleted();
     stopTimer();
+    if (m_gl) {
+        for (auto layer : m_layers)
+            m_gl->deleteTexture(layer.value);
+    }
+    m_layers.clear();
 }
 
 void SimulatedXRDevice::stopTimer()
@@ -99,6 +115,7 @@
         update();
 
     FrameData data;
+    data.shouldRender = true;
     if (m_viewerOrigin) {
         data.origin = *m_viewerOrigin;
         data.isTrackingValid = true;
@@ -122,6 +139,9 @@
         data.views.append(view);
     }
 
+    for (auto& layer : m_layers)
+        data.layers.add(layer.key, FrameData::LayerData { .opaqueTexture = layer.value });
+
     if (m_FrameCallback)
         m_FrameCallback(WTFMove(data));
 }
@@ -133,6 +153,36 @@
         m_frameTimer.startOneShot(FakeXRFrameTime);
 }
 
+Optional<PlatformXR::LayerHandle> SimulatedXRDevice::createLayerProjection(uint32_t width, uint32_t height, bool alpha)
+{
+    using GL = GraphicsContextGL;
+    if (!m_gl)
+        return WTF::nullopt;
+    PlatformXR::LayerHandle handle = ++m_layerIndex;
+    auto texture = m_gl->createTexture();
+    auto colorFormat = alpha ? GL::RGBA8 : GL::RGB8;
+
+    m_gl->bindTexture(GL::TEXTURE_2D, texture);
+    m_gl->texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_S, GL::CLAMP_TO_EDGE);
+    m_gl->texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_T, GL::CLAMP_TO_EDGE);
+    m_gl->texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MIN_FILTER, GL::LINEAR);
+    m_gl->texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MAG_FILTER, GL::LINEAR);
+    m_gl->texStorage2D(GL::TEXTURE_2D, 1, colorFormat, width, height);
+
+    m_layers.add(handle, texture);
+    return handle;    
+}
+
+void SimulatedXRDevice::deleteLayer(PlatformXR::LayerHandle handle)
+{
+    auto it = m_layers.find(handle);
+    if (it != m_layers.end()) {
+        if (m_gl)
+            m_gl->deleteTexture(it->value);
+        m_layers.remove(it);
+    }
+}
+
 Vector<PlatformXR::Device::ViewData> SimulatedXRDevice::views(PlatformXR::SessionMode mode) const
 {
     if (mode == PlatformXR::SessionMode::ImmersiveVr)

Modified: trunk/Source/WebCore/testing/WebFakeXRDevice.h (274694 => 274695)


--- trunk/Source/WebCore/testing/WebFakeXRDevice.h	2021-03-19 01:15:22 UTC (rev 274694)
+++ trunk/Source/WebCore/testing/WebFakeXRDevice.h	2021-03-19 01:27:38 UTC (rev 274695)
@@ -38,6 +38,9 @@
 #include <wtf/Vector.h>
 
 namespace WebCore {
+class GLContext;
+class GraphicsContextGL;
+
 class FakeXRView final : public RefCounted<FakeXRView> {
 public:
     static Ref<FakeXRView> create(XREye eye) { return adoptRef(*new FakeXRView(eye)); }
@@ -81,12 +84,14 @@
     void scheduleOnNextFrame(Function<void()>&&);
 private:
     WebCore::IntSize recommendedResolution(PlatformXR::SessionMode) final;
-    void initializeTrackingAndRendering(PlatformXR::SessionMode) final { }
+    void initializeTrackingAndRendering(PlatformXR::SessionMode) final;
     void shutDownTrackingAndRendering() final;
     bool supportsSessionShutdownNotification() const final { return m_supportsShutdownNotification; }
     void initializeReferenceSpace(PlatformXR::ReferenceSpaceType) final { }
     Vector<PlatformXR::Device::ViewData> views(PlatformXR::SessionMode) const final;
     void requestFrame(RequestFrameCallback&&) final;
+    Optional<PlatformXR::LayerHandle> createLayerProjection(uint32_t width, uint32_t height, bool alpha) final;
+    void deleteLayer(PlatformXR::LayerHandle) final;
 
     void stopTimer();
     void frameTimerFired();
@@ -101,6 +106,9 @@
     RequestFrameCallback m_FrameCallback;
     Vector<Function<void()>> m_pendingUpdates;
     FrameData::StageParameters m_stageParameters;
+    RefPtr<WebCore::GraphicsContextGL> m_gl;
+    HashMap<PlatformXR::LayerHandle, PlatformGLObject> m_layers;
+    uint32_t m_layerIndex { 0 };
 };
 
 class WebFakeXRDevice final : public RefCounted<WebFakeXRDevice> {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to