blob: e8b1bbe7442e1ef3ac33c265f0799147f2a004d7 [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.
#ifndef DEVICE_VR_OPENXR_OPENXR_GRAPHICS_BINDING_H_
#define DEVICE_VR_OPENXR_OPENXR_GRAPHICS_BINDING_H_
#include <cstdint>
#include <vector>
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "device/vr/openxr/openxr_composition_layer.h"
#include "device/vr/openxr/openxr_layers.h"
#include "device/vr/openxr/openxr_swapchain_info.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "mojo/public/cpp/platform/platform_handle.h"
#include "third_party/openxr/src/include/openxr/openxr.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
namespace gfx {
class GpuFence;
} // namespace gfx
namespace gpu {
class SharedImageInterface;
} // namespace gpu
namespace viz {
class ContextProvider;
} // namespace viz
namespace device {
class OpenXrCompositionLayer;
class OpenXrExtensionEnumeration;
class OpenXrViewConfiguration;
// This class exists to provide an abstraction for the different rendering
// paths that can be taken by OpenXR (e.g. DirectX vs. GLES). Any OpenXr methods
// that need types specific for a given renderer type should go through this
// interface.
class OpenXrGraphicsBinding {
public:
// Gets the set of RequiredExtensions that need to be present on the platform.
static void GetRequiredExtensions(std::vector<const char*>& extensions);
// Gets any OptionalExtensions that should be enabled if present.
static std::vector<std::string> GetOptionalExtensions();
virtual ~OpenXrGraphicsBinding();
// Ensures that the GraphicsBinding is ready for use.
virtual bool Initialize(XrInstance instance, XrSystemId system) = 0;
// Called after the XrSession has been created.
void OnSessionCreated(XrSpace local_space, bool is_webgpu);
// Called when the XrSession is going to destroyed.
void OnSessionDestroyed(gpu::SharedImageInterface* sii);
// Gets a pointer to a platform-specific XrGraphicsBindingFoo. The pointer is
// guaranteed to live as long as this class does.
virtual const void* GetSessionCreateInfo() const = 0;
// Gets the format that we expect from the platform swapchain.
virtual int64_t GetSwapchainFormat(XrSession session) const = 0;
// Calls xrEnumerateSwapChain and updates the stored OpenXrSwapchainInfo
// available via `GetSwapChainImages`.
virtual XrResult EnumerateSwapchainImages(OpenXrCompositionLayer& layer) = 0;
// Returns whether or not the platform believes it can support using Shared
// buffers/images.
virtual bool CanUseSharedImages() const = 0;
// Called when a frame is going to end without any attempt at rendering, in
// case there is any early cleanup to do that would otherwise occur during
// `Render`.
virtual void CleanupWithoutSubmit() = 0;
// Returns the maximum texture size allowed to be created with the current
// graphics binding. Textures larger than this size may be truncated during
// cross-process transportation of the textures and result in one viewport
// being rendered on over half of the texture, which can lead to uncomfortable
// rendering artifacts.
virtual gfx::Size GetMaxTextureSize() = 0;
// Called to indicate which of Overlay and WebXR content is expected to be
// composited during calls to `Render`.
void SetOverlayAndWebXrVisibility(bool overlay_visible, bool webxr_visible);
// There are three different paths that submitting an image can take. In two
// of them, we provide the surface/image for the page to draw into. The third
// is only supported on Windows or via the overlay code and requires
// submitting a texture handle to us, which we don't own. The first two
// rendering methods will have their data tied to the active swapchain image,
// but for the third method, we don't have to do any lifecycle management and
// will just hold a reference to the latest submitted texture. It will be
// valid until we end the frame, but can then be overwritten independently
// during the cycle. Since this third code-path only exists on Windows we
// restrict this method to that platform.
#if BUILDFLAG(IS_WIN)
virtual void SetWebXrTexture(mojo::PlatformHandle texture_handle,
const gpu::SyncToken& sync_token,
const gfx::RectF& left,
const gfx::RectF& right) = 0;
#endif
// Much like the `SetWebXrTexture` path above, the texture submitted here is
// owned by the browser process with corresponding lifetime management
// and synchronization happening there. It's valid until we tell it we're done
// with the texture, but it's not tied to a swapchain info the same way that
// the page's textures are, so we provide this additional method and simply
// overwrite the overlay whenever we receive it.
virtual bool SetOverlayTexture(gfx::GpuMemoryBufferHandle texture,
const gpu::SyncToken& sync_token,
const gfx::RectF& left,
const gfx::RectF& right) = 0;
// Will be called when SetSwapchainImageSize is called, even if a change is
// not made, to allow child classes/concrete implementations to override any
// state that they may need to override as a result of the swapchain image
// size changing. Note that the caller is responsible for recreating the
// swapchain in response to this call, but it likely has not been recreated
// yet.
virtual void OnSwapchainImageSizeChanged(OpenXrCompositionLayer& layer) {}
// Called at the end of ActivateSwapchainImage. Allows Children to setup the
// appropriate image to be rendered to by, e.g. Render calls, if that needs
// to happen ahead of time.
virtual void OnSwapchainImageActivated(OpenXrCompositionLayer& layer,
gpu::SharedImageInterface* sii) = 0;
// Return if the graphics binding supports multiple XR layers.
virtual bool SupportsLayers() const = 0;
// Resizes the shared buffer for the given swapchain info if the transfer size
// has changed.
virtual void ResizeSharedBuffer(OpenXrCompositionLayer& layer,
OpenXrSwapchainInfo& swap_chain_info,
gpu::SharedImageInterface* sii) = 0;
// Called to indicate which graphics API produced the textures submitted to
// OpenXR. Does not affect the API used for compositing.
bool IsWebGPUSession() const { return webgpu_session_; }
// If the layer should be flipped, return a pointer to the
// XrCompositionLayerImageLayoutFB. Otherwise, return null. The return value
// should be set to the "next" field of the XrCompositionLayer* struct.
const void* GetFlipLayerLayout() const;
// We check if the base layer is using shared images.
bool IsUsingSharedImages() const;
// Called when context proivder is lost.
void OnContextProviderLost();
// Build an OpenXrLayers object that provides data needed for xrEndFrame
// (e.g. a list of XrCompositionLayerBaseHeader).
std::unique_ptr<OpenXrLayers> GetLayersForViewConfig(
OpenXrApiWrapper* openxr,
const OpenXrViewConfiguration& view_config) const;
// A few methods that only operate on the base layer.
// Create the XrSwapchain and swapchain images for the base layer.
XrResult CreateBaseLayerSwapchain(XrSession session, uint32_t sample_count);
// Clears the list of images allocated during `CreateBaseLayerSwapchain` and
// if a context_provider is provided and the Swapchain entries have had
// corresponding SharedImages created via `CreateBaseLayerSharedImages` will
// also clean up those SharedImages.
void DestroyBaseLayerSwapchain(gpu::SharedImageInterface* sii);
// Creates SharedImages for (and thus populates the mailbox holders of) all
// currently held OpenXrSwapchainInfo objects.
void CreateBaseLayerSharedImages(gpu::SharedImageInterface* sii);
// Returns the previously set swapchain image size, or 0,0 if one is not set.
gfx::Size GetProjectionLayerSwapchainImageSize();
// Sets the size of the swapchain images being used by the system. Does *not*
// cause a corresponding re-creation of the Swapchain or Shared Images; which
// should be driven by the caller. If a transfer size has not been specified
// yet, will also set the transfer size as well.
void SetProjectionLayerSwapchainImageSize(
const gfx::Size& swapchain_image_size);
// Return if the XrSwapchain is available.
bool HasBaseLayerColorSwapchain() const;
// Sets the size of the texture being used between the renderer process and
// our process. This is largely driven by the page and any framebuffer scaling
// it may apply. When rendering to the SwapchainImage scaling will be
// performed as necessary.
void SetProjectionLayerTransferSize(const gfx::Size& transfer_size);
// Performs a server wait on the provided gpu_fence. Returns true if it was
// able to successfully schedule and perform the wait, and false otherwise.
bool WaitOnBaseLayerFence(gfx::GpuFence& gpu_fence);
// Updates the active swapchain image size if the transfer size has changed.
// No-ops if there is currently no active swapchain image.
void UpdateProjectionLayerActiveSwapchainImageSize(
gpu::SharedImageInterface* sii);
// Build XR projection views for the base layer.
std::vector<XrCompositionLayerProjectionView> GetBaseLayerProjectionViews(
const OpenXrViewConfiguration& view_config) const;
// A few methods that operate on all layers.
// Acquire and activate swapchain images from the OpenXr system
XrResult ActivateSwapchainImages(gpu::SharedImageInterface* sii);
// Release the active swapchain images from the OpenXr system. This is called
// before calling EndFrame and will enable acquiring a new swapchain image for
// the next frame.
XrResult ReleaseActiveSwapchainImages();
// Populate the shared image data in XRFrameData.
void PopulateSharedImageData(mojom::XRFrameData& frame_data);
// Causes the GraphicsBinding to render the currently active swapchain image.
bool Render(const scoped_refptr<viz::ContextProvider>& context_provider,
const std::vector<LayerId>& updated_layers);
// Create a composition layer. The id is given in layer_data.
bool CreateCompositionLayer(
mojom::XRCompositionLayerDataPtr layer_data,
gpu::SharedImageInterface* shared_image_interface);
// Get a composition layer by its layer id. Returns nullptr
// if the layer id doesn't exist.
OpenXrCompositionLayer* GetCompositionLayer(LayerId layer_id);
// Destroy a composition layer.
void DestroyCompositionLayer(LayerId layer_id,
gpu::SharedImageInterface* sii);
// Specify the layers that should be rendered and should have shared
// images available.
void SetEnabledCompositionLayers(const std::vector<LayerId>& layer_ids,
XrSession session,
uint32_t swapchain_sample_count,
gpu::SharedImageInterface* sii);
protected:
explicit OpenXrGraphicsBinding(
const OpenXrExtensionEnumeration* extension_enum);
bool ShouldRenderBaseLayer() const;
// Performs a server wait on the provided gpu_fence. Returns true if it was
// able to successfully schedule and perform the wait, and false otherwise.
virtual bool WaitOnFence(OpenXrCompositionLayer& layer,
gfx::GpuFence& gpu_fence) = 0;
// Render a single layer.
virtual bool RenderLayer(
OpenXrCompositionLayer& layer,
const scoped_refptr<viz::ContextProvider>& context_provider) = 0;
// Creates SharedImages for (and thus populates the mailbox holders of) all
// currently held OpenXrSwapchainInfo objects.
virtual void CreateSharedImages(OpenXrCompositionLayer& layer,
gpu::SharedImageInterface* sii) = 0;
// Indicates whether the graphics binding expects the submitted image to need
// to be flipped when being submitted to the runtime.
virtual bool ShouldFlipSubmittedImage(
OpenXrCompositionLayer& layer) const = 0;
// Create a graphics binding specific data.
virtual std::unique_ptr<OpenXrCompositionLayer::GraphicsBindingData>
CreateLayerGraphicsBindingData() const = 0;
// Called when SetOverlayAndWebXrVisibility is called and the internal flags
// have been updated.
virtual void OnSetOverlayAndWebXrVisibility() {}
// Build XR projection views for a projection layer.
std::vector<XrCompositionLayerProjectionView> GetProjectionViews(
const OpenXrViewConfiguration& view_config,
OpenXrCompositionLayer& layer) const;
std::unique_ptr<OpenXrCompositionLayer> base_layer_;
// Each client created layer has a unique ID.
std::map<LayerId, std::unique_ptr<OpenXrCompositionLayer>> layers_;
// This sequence defines which layers should be composed.
std::vector<LayerId> layers_sequence_;
bool has_custom_projection_layer_ = false;
bool webgpu_session_ = false;
bool fb_composition_layer_ext_enabled_ = false;
bool webxr_visible_ = true;
bool overlay_visible_ = false;
// This will only be valid if `fb_composition_layer_ext_enabled_` is true.
XrCompositionLayerImageLayoutFB y_flip_layer_layout_;
};
} // namespace device
#endif // DEVICE_VR_OPENXR_OPENXR_GRAPHICS_BINDING_H_