0

WebGPU support for Windows OpenXR WebXR

This change enables WebGPU XRSessions on the Windows OpenXR backend by
enabling SharedBuffer support.

Bug: 359418629
Change-Id: Ia9c6784cf0fd836eaf4a078dcafb94aaa77992e0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5942102
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1378648}
This commit is contained in:
Brandon Jones
2024-11-05 22:08:26 +00:00
committed by Chromium LUCI CQ
parent 630cdd2a95
commit a1549c0d70
5 changed files with 123 additions and 27 deletions

@ -85,8 +85,8 @@ void OpenXrGraphicsBinding::PrepareViewConfigForRender(
// steps.
if (ShouldFlipSubmittedImage()) {
projection_view.subImage.imageRect.offset.y = 0;
projection_view.fov.angleUp = view.fov.angleDown;
projection_view.fov.angleDown = view.fov.angleUp;
projection_view.fov.angleUp = -view.fov.angleUp;
projection_view.fov.angleDown = -view.fov.angleDown;
}
}
}

@ -68,6 +68,11 @@ struct SwapChainInfo {
// When shared images are being used, there is a corresponding MailboxHolder
// and D3D11Fence for each D3D11 texture in the vector.
raw_ptr<ID3D11Texture2D> d3d11_texture = nullptr;
// If a shared handle cannot be created for the swap chain texture, a second
// texture which is shareable will be created and passed to the renderer
// proceess. When the frame is complete it will be copied to the swap chain
// texture prior to submission.
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_shared_texture = nullptr;
Microsoft::WRL::ComPtr<ID3D11Fence> d3d11_fence;
#elif BUILDFLAG(IS_ANDROID)
// Ideally this would be a gluint, but there are conflicting headers for GL
@ -233,7 +238,7 @@ class OpenXrGraphicsBinding {
// Called to indicate which graphics API produced the textures submitted to
// OpenXR. Does not affect the API used for compositing.
void SetWebGPUSession(bool is_webgpu) { webgpu_session_ = is_webgpu; }
bool IsWebGPUSession() { return webgpu_session_; }
bool IsWebGPUSession() const { return webgpu_session_; }
protected:
// Internal helper to clear the list of images allocated during

@ -85,7 +85,7 @@ int64_t OpenXrGraphicsBindingD3D11::GetSwapchainFormat(
// OpenXR's swapchain format expects to describe the texture content.
// The result of a swapchain image created from OpenXR API always contains a
// typeless texture. On the other hand, WebGL API uses CSS color convention
// that's sRGB. The RGBA typelss texture from OpenXR swapchain image leads to
// that's sRGB. The RGBA typeless texture from OpenXR swapchain image leads to
// a linear format render target view (reference to function
// D3D11TextureHelper::EnsureRenderTargetView in d3d11_texture_helper.cc).
// Therefore, the content in this openxr swapchain image is in sRGB format.
@ -127,7 +127,7 @@ base::span<SwapChainInfo> OpenXrGraphicsBindingD3D11::GetSwapChainImages() {
bool OpenXrGraphicsBindingD3D11::CanUseSharedImages() const {
// Put shared image feature behind a flag until remaining issues with overlays
// are resolved.
return !base::FeatureList::IsEnabled(device::features::kOpenXRSharedImages);
return base::FeatureList::IsEnabled(device::features::kOpenXRSharedImages);
}
void OpenXrGraphicsBindingD3D11::CreateSharedImages(
@ -162,10 +162,53 @@ void OpenXrGraphicsBindingD3D11::CreateSharedImages(
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
nullptr, &shared_handle);
if (FAILED(hr)) {
DLOG(ERROR) << "Unable to create shared handle for DXGIResource "
<< std::hex << hr;
return;
DLOG(ERROR) << "Unable to create shared handle for DXGIResource (0x"
<< std::hex << hr << "). Creating a separate shareable "
<< "texture instead.";
D3D11_TEXTURE2D_DESC desc;
desc.Width = texture2d_desc.Width;
desc.Height = texture2d_desc.Height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
desc.MiscFlags =
D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
texture_helper_->GetDevice();
hr = d3d11_device->CreateTexture2D(&desc, nullptr,
&swap_chain_info.d3d11_shared_texture);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to create shared texture (0x" << std::hex << hr
<< ")";
return;
}
hr = swap_chain_info.d3d11_shared_texture->QueryInterface(
IID_PPV_ARGS(&dxgi_resource));
if (FAILED(hr)) {
DLOG(ERROR) << "QueryInterface for IDXGIResource of shared texture "
"failed with error 0x"
<< std::hex << hr;
return;
}
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
nullptr, &shared_handle);
if (FAILED(hr)) {
DLOG(ERROR)
<< "Unable to create shared handle for fallback DXGIResource (0x"
<< std::hex << hr << ").";
}
}
gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle;
@ -182,13 +225,18 @@ void OpenXrGraphicsBindingD3D11::CreateSharedImages(
gfx::Size(texture2d_desc.Width, texture2d_desc.Height);
// The SharedImages created here will eventually be transferred to other
// processes to have their contents written by WebGL and read via GL by
// OpenXR.
const gpu::SharedImageUsageSet shared_image_usage =
// processes to have their contents written by WebGL or WebGPU.
// Readback by OpenXR is always via OpenGL.
gpu::SharedImageUsageSet shared_image_usage =
gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
gpu::SHARED_IMAGE_USAGE_GLES2_READ |
gpu::SHARED_IMAGE_USAGE_GLES2_WRITE;
if (IsWebGPUSession()) {
shared_image_usage |= gpu::SHARED_IMAGE_USAGE_WEBGPU_READ |
gpu::SHARED_IMAGE_USAGE_WEBGPU_WRITE;
}
swap_chain_info.shared_image = sii->CreateSharedImage(
{viz::SinglePlaneFormat::kRGBA_8888, buffer_size,
gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT709,
@ -265,6 +313,19 @@ bool OpenXrGraphicsBindingD3D11::WaitOnFence(gfx::GpuFence& gpu_fence) {
bool OpenXrGraphicsBindingD3D11::Render(
const scoped_refptr<viz::ContextProvider>& context_provider) {
const SwapChainInfo& swap_chain_image = GetActiveSwapchainImage();
if (swap_chain_image.d3d11_shared_texture) {
// If a separate shared texture is being used, copy it into the original
// swap chain texture prior to any further compositing. The shared texture
// should always be the same size and format as the swap chain texture, so
// a direct copy can be done rather than a render pass.
texture_helper_->GetDeviceContext()->CopyResource(
/*destination=*/swap_chain_image.d3d11_texture.get(),
/*source=*/swap_chain_image.d3d11_shared_texture.Get());
}
// Even if a shared image was copied, always perform a composite to account
// for any necessary overlays.
return texture_helper_->UpdateBackbufferSizes() &&
texture_helper_->CompositeToBackBuffer(context_provider);
}
@ -274,7 +335,7 @@ void OpenXrGraphicsBindingD3D11::CleanupWithoutSubmit() {
}
bool OpenXrGraphicsBindingD3D11::ShouldFlipSubmittedImage() {
return IsUsingSharedImages();
return IsUsingSharedImages() && !IsWebGPUSession();
}
void OpenXrGraphicsBindingD3D11::OnSwapchainImageSizeChanged() {
@ -287,7 +348,8 @@ void OpenXrGraphicsBindingD3D11::OnSwapchainImageActivated(
CHECK(active_swapchain_index() < color_swapchain_images_.size());
texture_helper_->SetBackbuffer(
color_swapchain_images_[active_swapchain_index()].d3d11_texture.get());
color_swapchain_images_[active_swapchain_index()].d3d11_texture.get(),
IsUsingSharedImages(), ShouldFlipSubmittedImage());
}
void OpenXrGraphicsBindingD3D11::SetOverlayAndWebXrVisibility(
@ -304,6 +366,7 @@ void OpenXrGraphicsBindingD3D11::SetWebXrTexture(
base::win::ScopedHandle scoped_handle = texture_handle.is_valid()
? texture_handle.TakeHandle()
: base::win::ScopedHandle();
texture_helper_->SetSourceTexture(std::move(scoped_handle), sync_token, left,
right);
}

@ -189,9 +189,10 @@ bool D3D11TextureHelper::CompositeToBackBuffer(
// Source texture is optional depending on whether we're using
// shared images for the destination.
if (!render_state_.source_.source_texture_ &&
!render_state_.overlay_.source_texture_)
if ((backbuffer_contains_source_ || !render_state_.source_.source_texture_) &&
!render_state_.overlay_.source_texture_) {
return true;
}
gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
if (!gl) {
@ -252,7 +253,9 @@ bool D3D11TextureHelper::CompositeToBackBuffer(
}
if (render_state_.overlay_.source_texture_ &&
(!render_state_.source_.source_texture_ || !source_visible_)) {
((!backbuffer_contains_source_ &&
!render_state_.source_.source_texture_) ||
!source_visible_)) {
// If we have an overlay, but no WebXR texture under it, clear the target
// first, since overlay may assume transparency.
float color_rgba[4] = {0, 0, 0, 1};
@ -410,51 +413,59 @@ bool D3D11TextureHelper::BindTarget() {
void PushVertRect(std::vector<Vertex2D>& data,
const gfx::RectF& rect,
const gfx::RectF& uv,
int target) {
int target,
bool y_flip) {
Vertex2D vert;
vert.target = target;
// If the texture is being flipped, the top and bottom Y UV coordinates need
// to be swapped.
float y_top = y_flip ? uv.y() + uv.height() : uv.y();
float y_bottom = y_flip ? uv.y() : uv.y() + uv.height();
vert.x = rect.x() * 2 - 1;
vert.y = rect.y() * 2 - 1;
vert.u = uv.x();
vert.v = uv.y();
vert.v = y_top;
data.push_back(vert);
vert.x = rect.x() * 2 - 1;
vert.y = (rect.y() + rect.height()) * 2 - 1;
vert.u = uv.x();
vert.v = uv.y() + uv.height();
vert.v = y_bottom;
data.push_back(vert);
vert.x = (rect.x() + rect.width()) * 2 - 1;
vert.y = (rect.y() + rect.height()) * 2 - 1;
vert.u = uv.x() + uv.width();
vert.v = uv.y() + uv.height();
vert.v = y_bottom;
data.push_back(vert);
vert.x = rect.x() * 2 - 1;
vert.y = rect.y() * 2 - 1;
vert.u = uv.x();
vert.v = uv.y();
vert.v = y_top;
data.push_back(vert);
vert.x = (rect.x() + rect.width()) * 2 - 1;
vert.y = (rect.y() + rect.height()) * 2 - 1;
vert.u = uv.x() + uv.width();
vert.v = uv.y() + uv.height();
vert.v = y_bottom;
data.push_back(vert);
vert.x = (rect.x() + rect.width()) * 2 - 1;
vert.y = rect.y() * 2 - 1;
vert.u = uv.x() + uv.width();
vert.v = uv.y();
vert.v = y_top;
data.push_back(vert);
}
bool D3D11TextureHelper::UpdateVertexBuffer(LayerData& layer) {
std::vector<Vertex2D> vertex_data;
PushVertRect(vertex_data, target_left_, layer.left_, 0);
PushVertRect(vertex_data, target_right_, layer.right_, 1);
PushVertRect(vertex_data, target_left_, layer.left_, 0,
backbuffer_y_flipped_);
PushVertRect(vertex_data, target_right_, layer.right_, 1,
backbuffer_y_flipped_);
render_state_.d3d11_device_context_->UpdateSubresource(
render_state_.vertex_buffer_.Get(), 0, nullptr, vertex_data.data(),
sizeof(Vertex2D), vertex_data.size());
@ -619,11 +630,15 @@ bool D3D11TextureHelper::UpdateBackbufferSizes() {
}
void D3D11TextureHelper::SetBackbuffer(
Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer) {
Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer,
bool contains_source,
bool y_flipped) {
if (render_state_.target_texture_ != back_buffer) {
render_state_.render_target_view_ = nullptr;
}
render_state_.target_texture_ = back_buffer;
backbuffer_contains_source_ = contains_source;
backbuffer_y_flipped_ = y_flipped;
}
Microsoft::WRL::ComPtr<IDXGIAdapter> D3D11TextureHelper::GetAdapter() {
@ -646,6 +661,12 @@ Microsoft::WRL::ComPtr<ID3D11Device> D3D11TextureHelper::GetDevice() {
return render_state_.d3d11_device_;
}
Microsoft::WRL::ComPtr<ID3D11DeviceContext>
D3D11TextureHelper::GetDeviceContext() {
EnsureInitialized();
return render_state_.d3d11_device_context_;
}
bool D3D11TextureHelper::EnsureInitialized() {
if (render_state_.d3d11_device_ &&
SUCCEEDED(render_state_.d3d11_device_->GetDeviceRemovedReason()))

@ -35,6 +35,7 @@ class D3D11TextureHelper {
bool CompositeToBackBuffer(
const scoped_refptr<viz::ContextProvider>& context_provider);
void SetSourceTexture(base::win::ScopedHandle texture_handle,
const gpu::SyncToken& sync_token,
gfx::RectF left,
@ -51,8 +52,11 @@ class D3D11TextureHelper {
force_viewport_ = true;
}
gfx::Size BackBufferSize() { return target_size_; }
void SetBackbuffer(Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer);
void SetBackbuffer(Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer,
bool contains_source = false,
bool y_flipped = false);
Microsoft::WRL::ComPtr<ID3D11Device> GetDevice();
Microsoft::WRL::ComPtr<ID3D11DeviceContext> GetDeviceContext();
void SetDefaultSize(gfx::Size size) { default_size_ = size; }
@ -114,6 +118,9 @@ class D3D11TextureHelper {
bool bgra_ = false;
bool force_viewport_ = false;
bool backbuffer_contains_source_ = false;
bool backbuffer_y_flipped_ = false;
gfx::RectF target_left_; // 0 to 1 in each direction
gfx::RectF target_right_; // 0 to 1 in each direction
gfx::Size target_size_;