blob: 602b01e2afb8d8b9506ff0b9cb50cbdc1149231c [file] [log] [blame]
// 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.
#include "chrome/browser/vr/graphics_delegate.h"
#include <vector>
#include "base/check.h"
#include "base/notimplemented.h"
#include "base/numerics/angle_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/vr/fov_rectangle.h"
#include "chrome/browser/vr/frame_type.h"
#include "chrome/browser/vr/model/camera_model.h"
#include "chrome/browser/vr/render_info.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/transform.h"
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/vr/graphics_delegate_win.h"
#elif BUILDFLAG(IS_ANDROID)
#include "chrome/browser/vr/graphics_delegate_android.h"
#endif
namespace vr {
namespace {
constexpr float kZNear = 0.1f;
constexpr float kZFar = 10000.0f;
CameraModel CameraModelViewProjFromXRView(
const device::mojom::XRViewPtr& view) {
CameraModel model = {};
// TODO(crbug.com/40684534): mojo space is currently equivalent to
// world space, so the view matrix is world_from_view.
model.view_matrix = view->geometry->mojo_from_view;
bool is_invertible = model.view_matrix.GetInverse(&model.view_matrix);
DCHECK(is_invertible);
const auto& fov = view->geometry->field_of_view;
float up_tan = tanf(base::DegToRad(fov->up_degrees));
float left_tan = tanf(base::DegToRad(fov->left_degrees));
float right_tan = tanf(base::DegToRad(fov->right_degrees));
float down_tan = tanf(base::DegToRad(fov->down_degrees));
float x_scale = 2.0f / (left_tan + right_tan);
float y_scale = 2.0f / (up_tan + down_tan);
// clang-format off
gfx::Transform proj_matrix = gfx::Transform::RowMajor(
x_scale, 0, -((left_tan - right_tan) * x_scale * 0.5), 0,
0, y_scale, ((up_tan - down_tan) * y_scale * 0.5), 0,
0, 0, (kZFar + kZNear) / (kZNear - kZFar),
2 * kZFar * kZNear / (kZNear - kZFar),
0, 0, -1, 0);
// clang-format on
model.view_proj_matrix = proj_matrix * model.view_matrix;
return model;
}
} // namespace
std::unique_ptr<GraphicsDelegate> GraphicsDelegate::Create() {
#if BUILDFLAG(IS_WIN)
return std::make_unique<GraphicsDelegateWin>();
#elif BUILDFLAG(IS_ANDROID)
return std::make_unique<GraphicsDelegateAndroid>();
#else
NOTIMPLEMENTED();
return nullptr;
#endif
}
GraphicsDelegate::GraphicsDelegate() = default;
GraphicsDelegate::~GraphicsDelegate() = default;
float GraphicsDelegate::GetZNear() {
return kZNear;
}
void GraphicsDelegate::SetXrViews(
const std::vector<device::mojom::XRViewPtr>& views) {
// Store the first left and right views.
for (auto& view : views) {
if (view->eye == device::mojom::XREye::kLeft) {
left_ = view.Clone();
} else if (view->eye == device::mojom::XREye::kRight) {
right_ = view.Clone();
}
}
DCHECK(left_);
DCHECK(right_);
}
gfx::RectF GraphicsDelegate::GetLeft() {
gfx::Size size = GetTextureSize();
return gfx::RectF(
0, 0, static_cast<float>(left_->viewport.width()) / size.width(),
static_cast<float>(left_->viewport.height()) / size.height());
}
gfx::RectF GraphicsDelegate::GetRight() {
gfx::Size size = GetTextureSize();
return gfx::RectF(
static_cast<float>(left_->viewport.width()) / size.width(), 0,
static_cast<float>(right_->viewport.width()) / size.width(),
static_cast<float>(right_->viewport.height()) / size.height());
}
FovRectangles GraphicsDelegate::GetRecommendedFovs() {
DCHECK(left_);
DCHECK(right_);
FovRectangle left = {
left_->geometry->field_of_view->left_degrees,
left_->geometry->field_of_view->right_degrees,
left_->geometry->field_of_view->down_degrees,
left_->geometry->field_of_view->up_degrees,
};
FovRectangle right = {
right_->geometry->field_of_view->left_degrees,
right_->geometry->field_of_view->right_degrees,
right_->geometry->field_of_view->down_degrees,
right_->geometry->field_of_view->up_degrees,
};
return std::pair<FovRectangle, FovRectangle>(left, right);
}
RenderInfo GraphicsDelegate::GetRenderInfo(FrameType frame_type,
const gfx::Transform& head_pose) {
RenderInfo info;
info.head_pose = head_pose;
CameraModel left = CameraModelViewProjFromXRView(left_);
left.eye_type = kLeftEye;
left.viewport =
gfx::Rect(0, 0, left_->viewport.width(), left_->viewport.height());
info.left_eye_model = left;
CameraModel right = CameraModelViewProjFromXRView(right_);
right.eye_type = kRightEye;
right.viewport =
gfx::Rect(left_->viewport.width(), 0, right_->viewport.width(),
right_->viewport.height());
info.right_eye_model = right;
cached_info_ = info;
return info;
}
RenderInfo GraphicsDelegate::GetOptimizedRenderInfoForFovs(
const FovRectangles& fovs) {
RenderInfo info = cached_info_;
// TODO(billorr): consider optimizing overlays to save texture size.
// For now, we use a full-size texture when we could get by with less.
return info;
}
gfx::Size GraphicsDelegate::GetTextureSize() {
int width = left_->viewport.width() + right_->viewport.width();
int height = std::max(left_->viewport.height(), right_->viewport.height());
return gfx::Size(width, height);
}
} // namespace vr