// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_ANDROID_CARDBOARD_CARDBOARD_RENDER_LOOP_H_
#define DEVICE_VR_ANDROID_CARDBOARD_CARDBOARD_RENDER_LOOP_H_
#include <memory>
#include "base/android/java_handler_thread.h"
#include "base/memory/scoped_refptr.h"
#include "device/vr/android/cardboard/scoped_cardboard_objects.h"
#include "device/vr/android/mailbox_to_surface_bridge.h"
#include "device/vr/android/web_xr_presentation_state.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "gpu/ipc/common/surface_handle.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "ui/display/display.h"
#include "ui/gfx/native_widget_types.h"
namespace gl {
class GLSurface;
class GLContext;
} // namespace gl
namespace device {
class CardboardImageTransport;
class CardboardImageTransportFactory;
class CardboardSdk;
using CardboardRequestSessionCallback =
base::OnceCallback<void(mojom::XRRuntimeSessionResultPtr)>;
class CardboardRenderLoop : public base::android::JavaHandlerThread,
public device::mojom::XRFrameDataProvider,
public device::mojom::XRSessionController,
public device::mojom::XRPresentationProvider {
public:
CardboardRenderLoop(std::unique_ptr<CardboardImageTransportFactory>
cardboard_image_transport_factory,
std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge);
~CardboardRenderLoop() override;
CardboardRenderLoop(const CardboardRenderLoop&) = delete;
CardboardRenderLoop& operator=(const CardboardRenderLoop&) = delete;
void CreateSession(CardboardRequestSessionCallback session_request_callback,
base::OnceClosure session_shutdown_callback,
CardboardSdk* cardboard_sdk,
gfx::AcceleratedWidget drawing_widget,
const gfx::Size& frame_size,
display::Display::Rotation display_rotation,
mojom::XRRuntimeSessionOptionsPtr options);
// mojom::XRFrameDataProvider
void GetFrameData(mojom::XRFrameDataRequestOptionsPtr options,
GetFrameDataCallback callback) override;
void GetEnvironmentIntegrationProvider(
mojo::PendingAssociatedReceiver<
device::mojom::XREnvironmentIntegrationProvider> environment_provider)
override;
// XRPresentationProvider
void UpdateLayerBounds(int16_t frame_index,
const gfx::RectF& left_bounds,
const gfx::RectF& right_bounds,
const gfx::Size& source_size) override;
void SubmitFrameMissing(int16_t frame_index, const gpu::SyncToken&) override;
void SubmitFrame(int16_t frame_index,
const gpu::MailboxHolder& mailbox,
base::TimeDelta time_waited) override;
void SubmitFrameDrawnIntoTexture(int16_t frame_index,
const gpu::SyncToken&,
base::TimeDelta time_waited) override;
// mojom::XRSessionController
void SetFrameDataRestricted(bool restricted) override;
void OnTriggerEvent(bool pressed);
device::mojom::XRInputSourceStatePtr GetInputSourceState();
base::WeakPtr<CardboardRenderLoop> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
protected:
void CleanUp() override;
private:
void OnCardboardImageTransportReady(bool success);
bool InitializeGl(gfx::AcceleratedWidget drawing_widget);
void OnBindingDisconnect();
void CloseBindingsIfOpen();
bool CanStartNewAnimatingFrame();
bool IsSubmitFrameExpected(int16_t frame_index);
void ProcessFrameFromMailbox(int16_t frame_index,
const gpu::MailboxHolder& mailbox);
void ProcessFrameDrawnIntoTexture(const gpu::SyncToken& sync_token);
void OnWebXrTokenSignaled(std::unique_ptr<gfx::GpuFence> gpu_fence);
void TransitionProcessingFrameToRendering();
void ClearRenderingFrame(WebXrFrame* frame);
void RenderFrame(const gfx::Transform& uv_transform);
void FinishFrame(int16_t frame_index);
void FinishRenderingFrame(WebXrFrame* frame = nullptr);
void Pause();
void Resume();
// These are only alive until CreateSession is called
std::unique_ptr<CardboardImageTransportFactory>
cardboard_image_transport_factory_;
std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge_;
CardboardRequestSessionCallback session_request_callback_;
base::OnceClosure session_shutdown_callback_;
// Rendering Parameters
gfx::Size texture_size_ = {0, 0};
std::unique_ptr<CardboardImageTransport> cardboard_image_transport_;
std::unique_ptr<WebXrPresentationState> webxr_;
scoped_refptr<gl::GLSurface> surface_;
scoped_refptr<gl::GLContext> context_;
gfx::RectF left_bounds_;
gfx::RectF right_bounds_;
// Input Parameters
bool trigger_pressed_ = false;
bool trigger_clicked_ = false;
// Owned by our parent (cardboard_device)
raw_ptr<CardboardSdk> cardboard_sdk_;
// Session Controllers
mojo::Receiver<mojom::XRFrameDataProvider> frame_data_receiver_{this};
mojo::Receiver<mojom::XRSessionController> session_controller_receiver_{this};
mojo::Receiver<device::mojom::XRPresentationProvider> presentation_receiver_{
this};
mojo::Remote<device::mojom::XRPresentationClient> submit_client_;
// Stored Mojo data
mojom::XRViewPtr left_eye_;
mojom::XRViewPtr right_eye_;
std::unordered_set<device::mojom::XRSessionFeature> enabled_features_;
// Session State
bool pending_shutdown_ = false;
bool restrict_frame_data_ = false;
bool is_paused_ = false;
internal::ScopedCardboardObject<CardboardHeadTracker*> head_tracker_;
// This closure saves arguments for the next GetFrameData call, including a
// mojo callback. Must remain owned by CardboardRenderLoop, don't pass it off
// to the task runner directly. Storing the mojo getframedata callback in a
// closure owned by the task runner would lead to inconsistent state on
// session shutdown. See https://crbug.com/1065572.
base::OnceClosure pending_getframedata_;
// Must be last.
base::WeakPtrFactory<CardboardRenderLoop> weak_ptr_factory_{this};
};
} // namespace device
#endif // DEVICE_VR_ANDROID_CARDBOARD_CARDBOARD_RENDER_LOOP_H_