blob: ffe254a5e14b78f43483e73027136dde335425b8 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/openxr/android/openxr_light_estimator_android.h"
#include <memory>
#include <set>
#include "base/containers/flat_set.h"
#include "base/no_destructor.h"
#include "base/trace_event/trace_event.h"
#include "device/vr/openxr/openxr_extension_helper.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "third_party/openxr/dev/xr_android.h"
#include "third_party/openxr/src/include/openxr/openxr.h"
namespace device {
OpenXrLightEstimatorAndroid::OpenXrLightEstimatorAndroid(
const OpenXrExtensionHelper& extension_helper,
XrSession session,
XrSpace mojo_space)
: extension_helper_(extension_helper),
session_(session),
mojo_space_(mojo_space) {
XrLightEstimatorCreateInfoANDROID create_info{
XR_TYPE_LIGHT_ESTIMATOR_CREATE_INFO_ANDROID};
extension_helper_->ExtensionMethods().xrCreateLightEstimatorANDROID(
session_, &create_info, &light_estimator_);
}
OpenXrLightEstimatorAndroid::~OpenXrLightEstimatorAndroid() {
if (light_estimator_ != XR_NULL_HANDLE) {
XrResult result =
extension_helper_->ExtensionMethods().xrDestroyLightEstimatorANDROID(
light_estimator_);
if (XR_FAILED(result)) {
LOG(ERROR) << __func__ << " Failed to destroy light estimator.";
}
light_estimator_ = XR_NULL_HANDLE;
}
}
mojom::XRLightEstimationDataPtr OpenXrLightEstimatorAndroid::GetLightEstimate(
XrTime frame_time) {
if (light_estimator_ == XR_NULL_HANDLE) {
return nullptr;
}
TRACE_EVENT0("xr", "GetLightEstimate");
XrLightEstimateGetInfoANDROID estimate_info = {
XR_TYPE_LIGHT_ESTIMATE_GET_INFO_ANDROID};
estimate_info.space = mojo_space_;
estimate_info.time = frame_time;
XrDirectionalLightANDROID directional_light = {
XR_TYPE_DIRECTIONAL_LIGHT_ANDROID};
XrSphericalHarmonicsANDROID ambient_harmonics = {
XR_TYPE_SPHERICAL_HARMONICS_ANDROID};
ambient_harmonics.kind = XR_SPHERICAL_HARMONICS_KIND_AMBIENT_ANDROID;
ambient_harmonics.next = &directional_light;
XrLightEstimateANDROID estimate = {XR_TYPE_LIGHT_ESTIMATE_ANDROID};
estimate.next = &ambient_harmonics;
XrResult result =
extension_helper_->ExtensionMethods().xrGetLightEstimateANDROID(
light_estimator_, &estimate_info, &estimate);
if (XR_FAILED(result) ||
estimate.state != XR_LIGHT_ESTIMATE_STATE_VALID_ANDROID ||
ambient_harmonics.state != XR_LIGHT_ESTIMATE_STATE_VALID_ANDROID ||
directional_light.state != XR_LIGHT_ESTIMATE_STATE_VALID_ANDROID) {
return nullptr;
}
auto light_estimation_data = mojom::XRLightEstimationData::New();
light_estimation_data->light_probe = device::mojom::XRLightProbe::New();
auto& light_probe = light_estimation_data->light_probe;
light_probe->spherical_harmonics = device::mojom::XRSphericalHarmonics::New();
auto& spherical_harmonics = light_probe->spherical_harmonics;
constexpr size_t kNumShCoefficients = 9;
constexpr size_t kNumChannels = 3;
constexpr size_t kRedChannel = 0;
constexpr size_t kGreenChannel = 1;
constexpr size_t kBlueChannel = 2;
base::span<float[kNumChannels], kNumShCoefficients> coefficients =
base::span(ambient_harmonics.coefficients);
spherical_harmonics->coefficients.reserve(coefficients.size());
for (auto& coefficient : coefficients) {
base::span<float, kNumChannels> coefficient_data = base::span(coefficient);
spherical_harmonics->coefficients.emplace_back(
coefficient_data[kRedChannel], coefficient_data[kGreenChannel],
coefficient_data[kBlueChannel]);
}
light_probe->main_light_intensity = {directional_light.intensity.x,
directional_light.intensity.y,
directional_light.intensity.z};
light_probe->main_light_direction = gfx::Vector3dF(
directional_light.direction.x, directional_light.direction.y,
directional_light.direction.z);
return light_estimation_data;
}
OpenXrLightEstimatorAndroidFactory::OpenXrLightEstimatorAndroidFactory() =
default;
OpenXrLightEstimatorAndroidFactory::~OpenXrLightEstimatorAndroidFactory() =
default;
const base::flat_set<std::string_view>&
OpenXrLightEstimatorAndroidFactory::GetRequestedExtensions() const {
static base::NoDestructor<base::flat_set<std::string_view>> kExtensions(
{XR_ANDROID_LIGHT_ESTIMATION_EXTENSION_NAME});
return *kExtensions;
}
std::set<device::mojom::XRSessionFeature>
OpenXrLightEstimatorAndroidFactory::GetSupportedFeatures() const {
if (!IsEnabled()) {
return {};
}
return {device::mojom::XRSessionFeature::LIGHT_ESTIMATION};
}
void OpenXrLightEstimatorAndroidFactory::CheckAndUpdateEnabledState(
const OpenXrExtensionEnumeration* extension_enum,
XrInstance instance,
XrSystemId system) {
if (!AreAllRequestedExtensionsSupported(extension_enum)) {
SetEnabled(false);
return;
}
XrSystemLightEstimationPropertiesANDROID light_estimation_properties{
XR_TYPE_SYSTEM_LIGHT_ESTIMATION_PROPERTIES_ANDROID};
XrSystemProperties system_properties{XR_TYPE_SYSTEM_PROPERTIES};
system_properties.next = &light_estimation_properties;
bool lighting_supported = false;
XrResult result = xrGetSystemProperties(instance, system, &system_properties);
if (XR_SUCCEEDED(result)) {
lighting_supported = light_estimation_properties.supportsLightEstimation;
}
SetEnabled(lighting_supported);
}
std::unique_ptr<OpenXrLightEstimator>
OpenXrLightEstimatorAndroidFactory::CreateLightEstimator(
const OpenXrExtensionHelper& extension_helper,
XrSession session,
XrSpace mojo_space) const {
bool is_supported = IsEnabled();
DVLOG(2) << __func__ << " is_supported=" << is_supported;
if (is_supported) {
return std::make_unique<OpenXrLightEstimatorAndroid>(extension_helper,
session, mojo_space);
}
return nullptr;
}
} // namespace device