Skip to content

Commit

Permalink
PlatformXR::Device should inherit from ThreadSafeRefCountedAndCanMake…
Browse files Browse the repository at this point in the history
…ThreadSafeWeakPtr<>

https://bugs.webkit.org/show_bug.cgi?id=258494
<rdar://111265303>

Reviewed by Alex Christensen.

* Source/WebCore/Modules/webxr/WebXRSession.cpp:
(WebCore::WebXRSession::WebXRSession):
- Use `device` argument instead of calling m_device.get().
(WebCore::WebXRSession::~WebXRSession):
(WebCore::WebXRSession::referenceSpaceIsSupported const):
(WebCore::WebXRSession::requestReferenceSpace):
(WebCore::WebXRSession::recommendedWebGLFramebufferResolution const):
(WebCore::WebXRSession::supportsViewportScaling const):
(WebCore::WebXRSession::shutdown):
(WebCore::WebXRSession::didCompleteShutdown):
(WebCore::WebXRSession::requestFrameIfNeeded):
(WebCore::WebXRSession::onFrame):
* Source/WebCore/Modules/webxr/WebXRSession.h:
* Source/WebCore/Modules/webxr/WebXRSystem.cpp:
(WebCore::WebXRSystem::WebXRSystem):
- Switch to use DummyInlineDevice::create().
(WebCore::WebXRSystem::ensureImmersiveXRDeviceIsSelected):
(WebCore::WebXRSystem::obtainCurrentDevice):
(WebCore::WebXRSystem::isSessionSupported):
(WebCore::WebXRSystem::resolveRequestedFeatures const):
(WebCore::WebXRSystem::resolveFeaturePermissions const):
(WebCore::WebXRSystem::requestSession):
(WebCore::WebXRSystem::unregisterSimulatedXRDeviceForTesting):
(WebCore::WebXRSystem::DummyInlineDevice::create): Add.
* Source/WebCore/Modules/webxr/WebXRSystem.h:
(WebCore::WebXRSystem::hasActiveImmersiveXRDevice):
- Switch from using `PlatformXR::Device*` to
  `ThreadSafeWeakPtr<PlatformXR::Device>`.
(WebCore::WebXRSystem::DummyInlineDevice::create): Add.

* Source/WebCore/platform/xr/PlatformXR.h:
- Change PlatformXR::Device to inherit from
  ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<> since it currently
  inherits from ThreadSafeRefCounted<> and CanMakeWeakPtr<> separately.
(PlatformXR::Device::ref const): Add.
(PlatformXR::Device::deref const): Add.
(PlatformXR::Device::controlBlock const): Add.
- Add methods needed for
  ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<>.

* Source/WebCore/platform/xr/openxr/PlatformXROpenXR.cpp:
(PlatformXR::OpenXRDevice::~OpenXRDevice): Remove.
(PlatformXR::OpenXRDevice::endSession):
(PlatformXR::OpenXRDevice::updateInteractionProfile):
- Switch from using `WeakPtr<PlatformXR::Device>` to
  `ThreadSafeWeakPtr<PlatformXR::Device>`.
* Source/WebCore/platform/xr/openxr/PlatformXROpenXR.h:
(WebCore::PlatformXR::OpenXRDevice::~OpenXRDevice):
- Make destructor virtual and define as default here.

* Source/WebCore/testing/WebFakeXRDevice.cpp:
(WebCore::SimulatedXRDevice::initializeTrackingAndRendering):
- Switch from using `WeakPtr<PlatformXR::Device>` to
  `ThreadSafeWeakPtr<PlatformXR::Device>`.
* Source/WebCore/testing/WebFakeXRDevice.h:
(WebCore::SimulatedXRDevice::~SimulatedXRDevice):
- Make destructor virtual.
* Source/WebKit/Shared/XR/XRDeviceProxy.cpp:
(WebKit::XRDeviceProxy::initializeTrackingAndRendering):
- Switch from using `WeakPtr<PlatformXR::Device>` to
  `ThreadSafeWeakPtr<PlatformXR::Device>`.

Canonical link: https://commits.webkit.org/267114@main
  • Loading branch information
David Kilzer authored and ddkilzer committed Aug 22, 2023
1 parent 03f7c0f commit 6740671
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 72 deletions.
48 changes: 29 additions & 19 deletions Source/WebCore/Modules/webxr/WebXRSession.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2020 Igalia S.L. All rights reserved.
* Copyright (C) 2022 Apple Inc. All rights reserved.
* Copyright (C) 2022-2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -68,18 +68,19 @@ WebXRSession::WebXRSession(Document& document, WebXRSystem& system, XRSessionMod
, m_timeOrigin(MonotonicTime::now())
, m_views(device.views(mode))
{
m_device->setTrackingAndRenderingClient(*this);
m_device->initializeTrackingAndRendering(document.securityOrigin().data(), mode, m_requestedFeatures);
device.setTrackingAndRenderingClient(*this);
device.initializeTrackingAndRendering(document.securityOrigin().data(), mode, m_requestedFeatures);

// https://immersive-web.github.io/webxr/#ref-for-dom-xrreferencespacetype-viewer%E2%91%A2
// Every session MUST support viewer XRReferenceSpaces.
m_device->initializeReferenceSpace(XRReferenceSpaceType::Viewer);
device.initializeReferenceSpace(XRReferenceSpaceType::Viewer);
}

WebXRSession::~WebXRSession()
{
if (!m_ended && m_device)
m_device->shutDownTrackingAndRendering();
auto device = m_device.get();
if (!m_ended && device)
device->shutDownTrackingAndRendering();
}

XREnvironmentBlendMode WebXRSession::environmentBlendMode() const
Expand Down Expand Up @@ -183,7 +184,8 @@ bool WebXRSession::referenceSpaceIsSupported(XRReferenceSpaceType type) const
return true;

// 4. If type is local or local-floor, and the XR device supports reporting orientation data, return true.
if (m_device->supportsOrientationTracking())
auto device = m_device.get();
if (device && device->supportsOrientationTracking())
return true;
}

Expand Down Expand Up @@ -226,7 +228,8 @@ void WebXRSession::requestReferenceSpace(XRReferenceSpaceType type, RequestRefer
return;
}
// 2.2. Set up any platform resources required to track reference spaces of type type.
m_device->initializeReferenceSpace(type);
if (auto device = m_device.get())
device->initializeReferenceSpace(type);

// 2.3. Queue a task to run the following steps:
queueTaskKeepingObjectAlive(*this, TaskSource::WebXR, [this, type, promise = WTFMove(promise)]() mutable {
Expand Down Expand Up @@ -306,16 +309,18 @@ IntSize WebXRSession::nativeWebGLFramebufferResolution() const
// https://immersive-web.github.io/webxr/#recommended-webgl-framebuffer-resolution
IntSize WebXRSession::recommendedWebGLFramebufferResolution() const
{
ASSERT(m_device);
return m_device->recommendedResolution(m_mode);
auto device = m_device.get();
ASSERT(device);
return device ? device->recommendedResolution(m_mode) : IntSize { };
}

// https://immersive-web.github.io/webxr/#view-viewport-modifiable
bool WebXRSession::supportsViewportScaling() const
{
ASSERT(m_device);
auto device = m_device.get();
ASSERT(device);
// Only immersive sessions support viewport scaling.
return m_mode == XRSessionMode::ImmersiveVr && m_device->supportsViewportScaling();
return m_mode == XRSessionMode::ImmersiveVr && device && device->supportsViewportScaling();
}

bool WebXRSession::isPositionEmulated() const
Expand Down Expand Up @@ -362,19 +367,20 @@ void WebXRSession::shutdown(InitiatedBySystem initiatedBySystem)
// 6.1. Releasing exclusive access to the XR device if session is an immersive session.
// 6.2. Deallocating any graphics resources acquired by session for presentation to the XR device.
// 6.3. Putting the XR device in a state such that a different source may be able to initiate a session with the same device if session is an immersive session.
if (m_device)
m_device->shutDownTrackingAndRendering();
auto device = m_device.get();
if (device)
device->shutDownTrackingAndRendering();

// If device will not report shutdown completion via the TrackingAndRenderingClient,
// complete the shutdown cleanup here.
if (!m_device || !m_device->supportsSessionShutdownNotification())
if (!device || !device->supportsSessionShutdownNotification())
didCompleteShutdown();
}

void WebXRSession::didCompleteShutdown()
{
if (m_device)
m_device->setTrackingAndRenderingClient(nullptr);
if (auto device = m_device.get())
device->setTrackingAndRenderingClient(nullptr);

// Resolve end promise from XRSession::end()
if (m_endPromise) {
Expand Down Expand Up @@ -535,8 +541,11 @@ void WebXRSession::requestFrameIfNeeded()
if (m_callbacks.isEmpty() || m_isDeviceFrameRequestPending)
return;

auto device = m_device.get();
if (!device)
return;
m_isDeviceFrameRequestPending = true;
m_device->requestFrame([this, protectedThis = Ref { *this }](auto&& frameData) {
device->requestFrame([this, protectedThis = Ref { *this }](auto&& frameData) {
m_isDeviceFrameRequestPending = false;
onFrame(WTFMove(frameData));
});
Expand Down Expand Up @@ -622,7 +631,8 @@ void WebXRSession::onFrame(PlatformXR::Device::FrameData&& frameData)
if (m_mode == XRSessionMode::ImmersiveVr && m_activeRenderState->baseLayer())
frameLayers.append(m_activeRenderState->baseLayer()->endFrame());

m_device->submitFrame(WTFMove(frameLayers));
if (auto device = m_device.get())
device->submitFrame(WTFMove(frameLayers));
}

requestFrameIfNeeded();
Expand Down
11 changes: 6 additions & 5 deletions Source/WebCore/Modules/webxr/WebXRSession.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2020 Igalia S.L. All rights reserved.
* Copyright (C) 2021-2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -66,16 +67,16 @@ class WebXRSession final : public RefCounted<WebXRSession>, public EventTarget,
using RefCounted<WebXRSession>::ref;
using RefCounted<WebXRSession>::deref;

using TrackingAndRenderingClient::weakPtrFactory;
using TrackingAndRenderingClient::WeakValueType;
using TrackingAndRenderingClient::WeakPtrImplType;
using PlatformXR::TrackingAndRenderingClient::weakPtrFactory;
using PlatformXR::TrackingAndRenderingClient::WeakValueType;
using PlatformXR::TrackingAndRenderingClient::WeakPtrImplType;

XREnvironmentBlendMode environmentBlendMode() const;
XRInteractionMode interactionMode() const;
XRVisibilityState visibilityState() const;
const WebXRRenderState& renderState() const;
const WebXRInputSourceArray& inputSources() const;
PlatformXR::Device* device() const { return m_device.get(); }
RefPtr<PlatformXR::Device> device() const { return m_device.get(); }

ExceptionOr<void> updateRenderState(const XRRenderStateInit&);
void requestReferenceSpace(XRReferenceSpaceType, RequestReferenceSpacePromise&&);
Expand Down Expand Up @@ -142,7 +143,7 @@ class WebXRSession final : public RefCounted<WebXRSession>, public EventTarget,

WebXRSystem& m_xrSystem;
XRSessionMode m_mode;
WeakPtr<PlatformXR::Device> m_device;
ThreadSafeWeakPtr<PlatformXR::Device> m_device;
FeatureList m_requestedFeatures;
RefPtr<WebXRRenderState> m_activeRenderState;
RefPtr<WebXRRenderState> m_pendingRenderState;
Expand Down
47 changes: 26 additions & 21 deletions Source/WebCore/Modules/webxr/WebXRSystem.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2020 Igalia S.L. All rights reserved.
* Copyright (C) 2022 Apple Inc. All rights reserved.
* Copyright (C) 2021-2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -40,7 +40,6 @@
#include "LocalDOMWindow.h"
#include "Navigator.h"
#include "Page.h"
#include "PlatformXR.h"
#include "RequestAnimationFrameCallback.h"
#include "SecurityOrigin.h"
#include "UserGestureIndicator.h"
Expand All @@ -67,9 +66,9 @@ Ref<WebXRSystem> WebXRSystem::create(Navigator& navigator)
WebXRSystem::WebXRSystem(Navigator& navigator)
: ActiveDOMObject(navigator.scriptExecutionContext())
, m_navigator(navigator)
, m_defaultInlineDevice(*navigator.scriptExecutionContext())
, m_defaultInlineDevice(DummyInlineDevice::create(*navigator.scriptExecutionContext()))
{
m_inlineXRDevice = m_defaultInlineDevice;
m_inlineXRDevice = m_defaultInlineDevice.get();
}

WebXRSystem::~WebXRSystem() = default;
Expand All @@ -88,7 +87,7 @@ void WebXRSystem::ensureImmersiveXRDeviceIsSelected(CompletionHandler<void()>&&
return;
}

if (m_activeImmersiveDevice) {
if (m_activeImmersiveDevice.get()) {
callback();
return;
}
Expand All @@ -109,7 +108,7 @@ void WebXRSystem::ensureImmersiveXRDeviceIsSelected(CompletionHandler<void()>&&
});

// https://immersive-web.github.io/webxr/#select-an-immersive-xr-device
auto* oldDevice = m_activeImmersiveDevice.get();
auto oldDevice = m_activeImmersiveDevice.get();
if (immersiveXRDevices.isEmpty()) {
m_activeImmersiveDevice = nullptr;
return;
Expand All @@ -120,13 +119,13 @@ void WebXRSystem::ensureImmersiveXRDeviceIsSelected(CompletionHandler<void()>&&
}

if (m_activeImmersiveSession && oldDevice && immersiveXRDevices.findIf([&](auto& entry) { return entry.ptr() == oldDevice; }) != notFound)
ASSERT(m_activeImmersiveDevice.get() == oldDevice);
ASSERT(m_activeImmersiveDevice.get().get() == oldDevice.get());
else {
// FIXME: implement a better UA selection mechanism if required.
m_activeImmersiveDevice = immersiveXRDevices.first().get();
}

if (isFirstXRDevicesEnumeration || m_activeImmersiveDevice.get() == oldDevice) {
if (isFirstXRDevicesEnumeration || m_activeImmersiveDevice.get().get() == oldDevice.get()) {
return;
}

Expand All @@ -136,20 +135,20 @@ void WebXRSystem::ensureImmersiveXRDeviceIsSelected(CompletionHandler<void()>&&
});
}

void WebXRSystem::obtainCurrentDevice(XRSessionMode mode, const JSFeatureList& requiredFeatures, const JSFeatureList& optionalFeatures, CompletionHandler<void(PlatformXR::Device*)>&& callback)
void WebXRSystem::obtainCurrentDevice(XRSessionMode mode, const JSFeatureList& requiredFeatures, const JSFeatureList& optionalFeatures, CompletionHandler<void(ThreadSafeWeakPtr<PlatformXR::Device>)>&& callback)
{
if (mode == XRSessionMode::ImmersiveAr || mode == XRSessionMode::ImmersiveVr) {
ensureImmersiveXRDeviceIsSelected([this, callback = WTFMove(callback)]() mutable {
callback(m_activeImmersiveDevice.get());
callback(m_activeImmersiveDevice);
});
return;
}
if (!requiredFeatures.isEmpty() || !optionalFeatures.isEmpty()) {
callback(m_inlineXRDevice.get());
callback(m_inlineXRDevice);
return;
}

callback(&m_defaultInlineDevice);
callback(m_defaultInlineDevice.get());
}


Expand All @@ -175,13 +174,14 @@ void WebXRSystem::isSessionSupported(XRSessionMode mode, IsSessionSupportedPromi
// 4.1 Ensure an immersive XR device is selected.
ensureImmersiveXRDeviceIsSelected([this, promise = WTFMove(promise), mode]() mutable {
// 4.2 If the immersive XR device is null, resolve promise with false and abort these steps.
if (!m_activeImmersiveDevice) {
auto activeImmersiveDevice = m_activeImmersiveDevice.get();
if (!activeImmersiveDevice) {
promise.resolve(false);
return;
}

// 4.3 If the immersive XR device's list of supported modes does not contain mode, resolve promise with false and abort these steps.
if (!m_activeImmersiveDevice->supports(mode)) {
if (!activeImmersiveDevice->supports(mode)) {
promise.resolve(false);
return;
}
Expand Down Expand Up @@ -290,7 +290,7 @@ bool WebXRSystem::isFeatureSupported(PlatformXR::SessionFeature feature, XRSessi
}

// https://immersive-web.github.io/webxr/#resolve-the-requested-features
std::optional<WebXRSystem::ResolvedRequestedFeatures> WebXRSystem::resolveRequestedFeatures(XRSessionMode mode, const XRSessionInit& init, PlatformXR::Device* device, JSC::JSGlobalObject& globalObject) const
std::optional<WebXRSystem::ResolvedRequestedFeatures> WebXRSystem::resolveRequestedFeatures(XRSessionMode mode, const XRSessionInit& init, RefPtr<PlatformXR::Device> device, JSC::JSGlobalObject& globalObject) const
{
// 1. Let consentRequired be an empty list of DOMString.
// 2. Let consentOptional be an empty list of DOMString.
Expand Down Expand Up @@ -384,7 +384,7 @@ std::optional<WebXRSystem::ResolvedRequestedFeatures> WebXRSystem::resolveReques
}

// https://immersive-web.github.io/webxr/#request-the-xr-permission
void WebXRSystem::resolveFeaturePermissions(XRSessionMode mode, const XRSessionInit& init, PlatformXR::Device* device, JSC::JSGlobalObject& globalObject, CompletionHandler<void(std::optional<FeatureList>&&)>&& completionHandler) const
void WebXRSystem::resolveFeaturePermissions(XRSessionMode mode, const XRSessionInit& init, RefPtr<PlatformXR::Device> device, JSC::JSGlobalObject& globalObject, CompletionHandler<void(std::optional<FeatureList>&&)>&& completionHandler) const
{
// 1. Set status's granted to an empty FrozenArray.
// 2. Let requiredFeatures be descriptor's requiredFeatures.
Expand Down Expand Up @@ -482,7 +482,7 @@ void WebXRSystem::requestSession(Document& document, XRSessionMode mode, const X
// 5.2 Let optionalFeatures be options' optionalFeatures.
// 5.3 Set device to the result of obtaining the current device for mode, requiredFeatures, and optionalFeatures.
// 5.4 Queue a task to perform the following steps:
obtainCurrentDevice(mode, init.requiredFeatures, init.optionalFeatures, [this, protectedDocument, immersive, init, mode, promise = WTFMove(promise)](auto* device) mutable {
obtainCurrentDevice(mode, init.requiredFeatures, init.optionalFeatures, [this, protectedDocument, immersive, init, mode, promise = WTFMove(promise)](ThreadSafeWeakPtr<PlatformXR::Device> weakDevice) mutable {
auto rejectPromiseWithNotSupportedError = makeScopeExit([&]() {
promise.reject(Exception { NotSupportedError });
m_pendingImmersiveSession = false;
Expand All @@ -492,6 +492,7 @@ void WebXRSystem::requestSession(Document& document, XRSessionMode mode, const X
// - Reject promise with a "NotSupportedError" DOMException.
// - If immersive is true, set pending immersive session to false.
// - Abort these steps.
auto device = weakDevice.get();
if (!device || !device->supports(mode))
return;

Expand Down Expand Up @@ -568,11 +569,11 @@ void WebXRSystem::unregisterSimulatedXRDeviceForTesting(PlatformXR::Device& devi

ASSERT(m_testingDevices);
bool removed = m_immersiveDevices.remove(device);
ASSERT_UNUSED(removed, removed || m_inlineXRDevice == &device);
if (m_activeImmersiveDevice == &device)
ASSERT_UNUSED(removed, removed || m_inlineXRDevice.get().get() == &device);
if (m_activeImmersiveDevice.get().get() == &device)
m_activeImmersiveDevice = nullptr;
if (m_inlineXRDevice == &device)
m_inlineXRDevice = m_defaultInlineDevice;
if (m_inlineXRDevice.get().get() == &device)
m_inlineXRDevice = m_defaultInlineDevice.get();
m_testingDevices--;
}

Expand Down Expand Up @@ -605,6 +606,10 @@ class InlineRequestAnimationFrameCallback final: public RequestAnimationFrameCal
Function<void()> m_callback;
};

Ref<WebXRSystem::DummyInlineDevice> WebXRSystem::DummyInlineDevice::create(ScriptExecutionContext& scriptExecutionContext)
{
return adoptRef(*new WebXRSystem::DummyInlineDevice(scriptExecutionContext));
}

WebXRSystem::DummyInlineDevice::DummyInlineDevice(ScriptExecutionContext& scriptExecutionContext)
: ContextDestructionObserver(&scriptExecutionContext)
Expand Down
Loading

0 comments on commit 6740671

Please sign in to comment.