blob: dbc90ae3e0018a8a8173d221e391a7e9d2d9ce76 [file] [log] [blame]
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <math.h>
#include "base/bind.h"
#include "base/containers/cxx20_erase.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/numerics/math_constants.h"
#include "base/time/time.h"
#include "device/vr/orientation/orientation_device.h"
#include "device/vr/orientation/orientation_session.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
#include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
#include "services/device/public/mojom/sensor_provider.mojom.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/quaternion.h"
#include "ui/gfx/geometry/vector3d_f.h"
namespace device {
using gfx::Quaternion;
using gfx::Vector3dF;
namespace {
static constexpr int kDefaultPumpFrequencyHz = 60;
display::Display::Rotation GetRotation() {
display::Screen* screen = display::Screen::GetScreen();
if (!screen) {
// If we can't get rotation we'll assume it's 0.
return display::Display::ROTATE_0;
}
return screen->GetPrimaryDisplay().rotation();
}
const std::vector<mojom::XRSessionFeature>& GetSupportedFeatures() {
static base::NoDestructor<std::vector<mojom::XRSessionFeature>>
kSupportedFeatures({
mojom::XRSessionFeature::REF_SPACE_VIEWER,
mojom::XRSessionFeature::REF_SPACE_LOCAL,
mojom::XRSessionFeature::REF_SPACE_LOCAL_FLOOR,
});
return *kSupportedFeatures;
}
} // namespace
VROrientationDevice::VROrientationDevice(mojom::SensorProvider* sensor_provider,
base::OnceClosure ready_callback)
: VRDeviceBase(mojom::XRDeviceId::ORIENTATION_DEVICE_ID),
ready_callback_(std::move(ready_callback)) {
DVLOG(2) << __func__;
sensor_provider->GetSensor(kOrientationSensorType,
base::BindOnce(&VROrientationDevice::SensorReady,
base::Unretained(this)));
SetVRDisplayInfo(mojom::VRDisplayInfo::New());
SetSupportedFeatures(GetSupportedFeatures());
}
VROrientationDevice::~VROrientationDevice() {
DVLOG(2) << __func__;
}
void VROrientationDevice::SensorReady(
device::mojom::SensorCreationResult,
device::mojom::SensorInitParamsPtr params) {
if (!params) {
// This means that there are no orientation sensors on this device.
HandleSensorError();
std::move(ready_callback_).Run();
return;
}
DVLOG(2) << __func__;
constexpr size_t kReadBufferSize = sizeof(device::SensorReadingSharedBuffer);
DCHECK_EQ(0u, params->buffer_offset % kReadBufferSize);
device::PlatformSensorConfiguration default_config =
params->default_configuration;
sensor_.Bind(std::move(params->sensor));
receiver_.Bind(std::move(params->client_receiver));
shared_buffer_reader_ = device::SensorReadingSharedBufferReader::Create(
std::move(params->memory), params->buffer_offset);
if (!shared_buffer_reader_) {
// If we cannot read data, we cannot supply a device.
HandleSensorError();
std::move(ready_callback_).Run();
return;
}
default_config.set_frequency(kDefaultPumpFrequencyHz);
sensor_.set_disconnect_handler(base::BindOnce(
&VROrientationDevice::HandleSensorError, base::Unretained(this)));
sensor_->ConfigureReadingChangeNotifications(false /* disabled */);
sensor_->AddConfiguration(
default_config,
base::BindOnce(&VROrientationDevice::OnSensorAddConfiguration,
base::Unretained(this)));
}
// Mojo callback for Sensor::AddConfiguration().
void VROrientationDevice::OnSensorAddConfiguration(bool success) {
if (!success) {
// Sensor config is not supported so we can't provide sensor events.
HandleSensorError();
} else {
// We're good to go.
available_ = true;
}
std::move(ready_callback_).Run();
}
void VROrientationDevice::RaiseError() {
HandleSensorError();
}
void VROrientationDevice::HandleSensorError() {
sensor_.reset();
shared_buffer_reader_.reset();
receiver_.reset();
}
void VROrientationDevice::RequestSession(
mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) {
DVLOG(2) << __func__;
DCHECK_EQ(options->mode, mojom::XRSessionMode::kInline);
// TODO(http://crbug.com/695937): Perform a check to see if sensors are
// available when RequestSession is called for non-immersive sessions.
mojo::PendingRemote<mojom::XRFrameDataProvider> data_provider;
mojo::PendingRemote<mojom::XRSessionController> controller;
magic_window_sessions_.push_back(std::make_unique<VROrientationSession>(
this, data_provider.InitWithNewPipeAndPassReceiver(),
controller.InitWithNewPipeAndPassReceiver()));
auto session_result = mojom::XRRuntimeSessionResult::New();
session_result->controller = std::move(controller);
session_result->session = mojom::XRSession::New();
auto* session = session_result->session.get();
session->data_provider = std::move(data_provider);
if (display_info_) {
session->display_info = display_info_.Clone();
}
session->device_config = device::mojom::XRSessionDeviceConfig::New();
session->enviroment_blend_mode =
device::mojom::XREnvironmentBlendMode::kOpaque;
session->interaction_mode = device::mojom::XRInteractionMode::kScreenSpace;
// Currently, the initial filtering of supported devices happens on the
// browser side (BrowserXRRuntimeImpl::SupportsFeature()), so if we have
// reached this point, it is safe to assume that all requested features are
// enabled.
// TODO(https://crbug.com/995377): revisit the approach when the bug is fixed.
session->enabled_features.insert(session->enabled_features.end(),
options->required_features.begin(),
options->required_features.end());
session->enabled_features.insert(session->enabled_features.end(),
options->optional_features.begin(),
options->optional_features.end());
std::move(callback).Run(std::move(session_result));
// The sensor may have been suspended, so resume it now.
sensor_->Resume();
}
void VROrientationDevice::EndMagicWindowSession(VROrientationSession* session) {
DVLOG(2) << __func__;
base::EraseIf(magic_window_sessions_,
[session](const std::unique_ptr<VROrientationSession>& item) {
return item.get() == session;
});
// If there are no more magic window sessions, suspend the sensor until we get
// a new one.
if (magic_window_sessions_.empty()) {
sensor_->Suspend();
}
}
void VROrientationDevice::GetInlineFrameData(
mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
// Orientation sessions should never be exclusive or presenting.
DCHECK(!HasExclusiveSession());
mojom::VRPosePtr pose = mojom::VRPose::New();
SensorReading latest_reading;
// If the reading fails just return the last pose that we got.
if (shared_buffer_reader_->GetReading(&latest_reading)) {
latest_pose_.set_x(latest_reading.orientation_quat.x);
latest_pose_.set_y(latest_reading.orientation_quat.y);
latest_pose_.set_z(latest_reading.orientation_quat.z);
latest_pose_.set_w(latest_reading.orientation_quat.w);
latest_pose_ =
WorldSpaceToUserOrientedSpace(SensorSpaceToWorldSpace(latest_pose_));
}
pose->orientation = latest_pose_;
mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
frame_data->pose = std::move(pose);
std::move(callback).Run(std::move(frame_data));
}
Quaternion VROrientationDevice::SensorSpaceToWorldSpace(Quaternion q) {
display::Display::Rotation rotation = GetRotation();
if (rotation == display::Display::ROTATE_90) {
// Rotate the sensor reading to account for the screen rotation.
q = q * Quaternion(Vector3dF(0, 0, 1), -base::kPiDouble / 2);
} else if (rotation == display::Display::ROTATE_270) {
// Rotate the sensor reading to account for the screen rotation the other
// way.
q = q * Quaternion(Vector3dF(0, 0, 1), base::kPiDouble / 2);
}
// Tilt the view up to have the y axis as the vertical axis instead of z
q = Quaternion(Vector3dF(1, 0, 0), -base::kPiDouble / 2) * q;
return q;
}
Quaternion VROrientationDevice::WorldSpaceToUserOrientedSpace(Quaternion q) {
if (!base_pose_) {
// Check that q is valid by checking if the length is not 0 (it should
// technically always be 1, but this accounts for rounding errors).
if (!(q.Length() > .1)) {
// q is invalid. Do not use for base pose.
return q;
}
// A base pose to read the initial forward direction off of.
base_pose_ = q;
// Extract the yaw from base pose to use as the base forward direction.
base_pose_->set_x(0);
base_pose_->set_z(0);
base_pose_ = base_pose_->Normalized();
}
// Adjust the base forward on the orientation to where the original forward
// was.
q = base_pose_->inverse() * q;
return q;
}
} // namespace device