blob: 8a3080be4383bc4971d9bf1494644f47e874b0d6 [file]
// Copyright 2025 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/openxr_composition_layer.h"
#include "device/vr/openxr/openxr_graphics_binding.h"
#include "device/vr/openxr/openxr_platform.h"
#include "device/vr/openxr/openxr_util.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
namespace device {
// static
OpenXrCompositionLayer::Type OpenXrCompositionLayer::GetTypeFromMojomData(
const mojom::XRLayerSpecificData& layer_specific_data) {
switch (layer_specific_data.which()) {
case mojom::XRLayerSpecificData::Tag::kProjection:
return Type::kProjection;
case mojom::XRLayerSpecificData::Tag::kQuad:
return Type::kQuad;
case mojom::XRLayerSpecificData::Tag::kCylinder:
return Type::kCylinder;
case mojom::XRLayerSpecificData::Tag::kEquirect:
return Type::kEquirect;
case mojom::XRLayerSpecificData::Tag::kCube:
return Type::kCube;
}
}
OpenXrCompositionLayer::OpenXrCompositionLayer(
mojom::XRCompositionLayerDataPtr layer_data,
OpenXrGraphicsBinding* graphics_binding,
std::unique_ptr<GraphicsBindingData> graphics_binding_data)
: graphics_binding_(graphics_binding),
graphics_binding_data_(std::move(graphics_binding_data)),
creation_data_(std::move(layer_data)) {
type_ = GetTypeFromMojomData(*creation_data_->mutable_data->layer_data);
// Projection layers will have same size as the base layer, and will
// be set later.
if (type_ != Type::kProjection) {
SetSwapchainImageSize(gfx::Size(read_only_data().texture_width,
read_only_data().texture_height));
}
}
OpenXrCompositionLayer::~OpenXrCompositionLayer() = default;
gfx::Size OpenXrCompositionLayer::GetSwapchainImageSize() {
return swapchain_image_size_;
}
void OpenXrCompositionLayer::SetSwapchainImageSize(
const gfx::Size& swapchain_image_size) {
swapchain_image_size_ = swapchain_image_size;
graphics_binding_->OnSwapchainImageSizeChanged(*this);
// By default assume that we're transferring something the same size as the
// swapchain image. However, if it's already been set, we don't want to
// override that.
if (transfer_size_.IsZero()) {
SetTransferSize(swapchain_image_size);
}
}
gfx::Size OpenXrCompositionLayer::GetTransferSize() {
return transfer_size_;
}
void OpenXrCompositionLayer::SetTransferSize(const gfx::Size& transfer_size) {
transfer_size_ = transfer_size;
}
void OpenXrCompositionLayer::SetSwapchainImages(
std::vector<OpenXrSwapchainInfo> images) {
CHECK(color_swapchain_images_.empty());
color_swapchain_images_ = std::move(images);
}
base::span<OpenXrSwapchainInfo> OpenXrCompositionLayer::GetSwapchainImages() {
return color_swapchain_images_;
}
base::span<const OpenXrSwapchainInfo>
OpenXrCompositionLayer::GetSwapchainImages() const {
return color_swapchain_images_;
}
XrResult OpenXrCompositionLayer::CreateSwapchain(XrSession session,
uint32_t sample_count) {
DCHECK(!HasColorSwapchain());
DCHECK(GetSwapchainImages().empty());
XrSwapchainCreateInfo swapchain_create_info = {XR_TYPE_SWAPCHAIN_CREATE_INFO};
swapchain_create_info.arraySize = 1;
swapchain_create_info.format = graphics_binding_->GetSwapchainFormat(session);
swapchain_create_info.createFlags = creation_data_->read_only_data->is_static
? XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT
: 0;
swapchain_create_info.width = swapchain_image_size_.width();
swapchain_create_info.height = swapchain_image_size_.height();
swapchain_create_info.mipCount = 1;
swapchain_create_info.faceCount = type_ == Type::kCube ? 6 : 1;
swapchain_create_info.sampleCount = sample_count;
swapchain_create_info.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
XrSwapchain color_swapchain;
RETURN_IF_XR_FAILED(
xrCreateSwapchain(session, &swapchain_create_info, &color_swapchain));
color_swapchain_ = color_swapchain;
needs_redraw_ = true;
RETURN_IF_XR_FAILED(graphics_binding_->EnumerateSwapchainImages(*this));
return XR_SUCCESS;
}
void OpenXrCompositionLayer::DestroySwapchain(gpu::SharedImageInterface* sii) {
// In case we still hold an active swapchain image.
ReleaseActiveSwapchainImage();
// Reset rendered state.
needs_redraw_ = false;
is_rendered_ = false;
// As long as we have a context provider we need to destroy any SharedImages
// that may exist.
for (OpenXrSwapchainInfo& info : GetSwapchainImages()) {
if (sii && info.shared_image && info.sync_token.HasData()) {
info.shared_image->UpdateDestructionSyncToken(info.sync_token);
info.shared_image.reset();
}
info.Clear();
}
if (color_swapchain_) {
xrDestroySwapchain(color_swapchain_);
color_swapchain_ = XR_NULL_HANDLE;
}
// Regardless of if we had a context provider or any shared images, we need to
// clear the list of SwapchainImages.
color_swapchain_images_.clear();
}
XrResult OpenXrCompositionLayer::ActivateSwapchainImage(
gpu::SharedImageInterface* sii) {
CHECK(!has_active_swapchain_image_);
XrSwapchainImageAcquireInfo acquire_info = {
XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO};
RETURN_IF_XR_FAILED(xrAcquireSwapchainImage(color_swapchain_, &acquire_info,
&active_swapchain_index_));
XrSwapchainImageWaitInfo wait_info = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
wait_info.timeout = XR_INFINITE_DURATION;
RETURN_IF_XR_FAILED(xrWaitSwapchainImage(color_swapchain_, &wait_info));
has_active_swapchain_image_ = true;
// The current active swapchain image has not yet been rendered.
is_rendered_ = false;
graphics_binding_->OnSwapchainImageActivated(*this, sii);
return XR_SUCCESS;
}
XrResult OpenXrCompositionLayer::ReleaseActiveSwapchainImage() {
if (!has_active_swapchain_image_) {
return XR_SUCCESS;
}
has_active_swapchain_image_ = false;
// Since `active_swapchain_index_` is a unit32_t there's not a good "invalid"
// number to set; so just leave it alone after clearing it.
XrSwapchainImageReleaseInfo release_info = {
XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
return xrReleaseSwapchainImage(color_swapchain_, &release_info);
}
OpenXrSwapchainInfo* OpenXrCompositionLayer::GetActiveSwapchainImage() {
if (!has_active_swapchain_image_ ||
active_swapchain_index_ >= color_swapchain_images_.size()) {
return nullptr;
}
// We don't do any index translation on the images returned from the system;
// so whatever the system says is the active swapchain image, it is in the
// same spot in our vector.
return &color_swapchain_images_[active_swapchain_index_];
}
bool OpenXrCompositionLayer::IsUsingSharedImages() const {
const auto swapchain_info = GetSwapchainImages();
return ((swapchain_info.size() > 1) && swapchain_info[0].shared_image);
}
LayerId OpenXrCompositionLayer::GetLayerId() const {
return creation_data_->read_only_data->layer_id;
}
void OpenXrCompositionLayer::UpdateMutableLayerData(
mojom::XRLayerMutableDataPtr data) {
CHECK_EQ(type_, GetTypeFromMojomData(*data->layer_data));
creation_data_->mutable_data = std::move(data);
}
void OpenXrCompositionLayer::UpdateActiveSwapchainImageSize(
gpu::SharedImageInterface* sii) {
if (has_active_swapchain_image_) {
graphics_binding_->ResizeSharedBuffer(*this, *GetActiveSwapchainImage(),
sii);
}
}
const gfx::Rect OpenXrCompositionLayer::GetSubImageViewport(
XrEyeVisibility eye) const {
gfx::Rect info{0, 0, static_cast<int>(read_only_data().texture_width),
static_cast<int>(read_only_data().texture_height)};
// When force_mono_presentation is true, the eye will be
// XR_EYE_VISIBILITY_BOTH, and we will still respect the layout and only use
// the left eye's viewport.
if (read_only_data().layout ==
device::mojom::XRLayerLayout::kStereoLeftRight) {
info.set_width(info.width() / 2);
if (eye == XR_EYE_VISIBILITY_RIGHT) {
info.set_x(info.width());
}
} else if (read_only_data().layout ==
device::mojom::XRLayerLayout::kStereoTopBottom) {
info.set_height(info.height() / 2);
if (eye == XR_EYE_VISIBILITY_RIGHT) {
info.set_y(info.height());
}
}
return info;
}
std::vector<XrEyeVisibility> OpenXrCompositionLayer::GetXrEyesForComposition()
const {
if (type_ == Type::kProjection) {
// Projection layer uses view configurations for eyes. See
// OpenXrGraphicsBinding::GetProjectionViews().
return {XR_EYE_VISIBILITY_BOTH};
}
if (mutable_data().force_mono_presentation) {
// force_mono_presentation forces the right eye to use the same
// layer configuration as the left eye.
return {XR_EYE_VISIBILITY_BOTH};
}
if (read_only_data().layout ==
device::mojom::XRLayerLayout::kStereoTopBottom ||
read_only_data().layout ==
device::mojom::XRLayerLayout::kStereoLeftRight ||
read_only_data().layout == device::mojom::XRLayerLayout::kStereo) {
return {XR_EYE_VISIBILITY_LEFT, XR_EYE_VISIBILITY_RIGHT};
}
return {XR_EYE_VISIBILITY_BOTH};
}
} // namespace device