Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove GL->d3d blit in HoloLens immersive mode #25855

Merged
merged 1 commit into from Mar 10, 2020

Conversation

@jdm
Copy link
Member

jdm commented Feb 27, 2020

Depends on:

These changes add two extra APIs for embedders to use when registering a WebXR device - one to allow running any closure as a task in the webgl thread, and one to register an arbitrary surface provider for a particular webxr session. When an openxr session is started, it can then obtain the webgl thread's d3d device from that thread's surfman device and ensure that openxr uses it.

Surface providers are traits that have their methods invoked by the webgl thread as part of the the normal swapchain operations. This allows the openxr surface provider to return surfaces that wrap the underlying openxr textures, which are valid in the webgl thread and can be used as the target of an opaque framebuffer.


  • ./mach build -d does not report any errors
  • ./mach test-tidy does not report any errors
  • These changes fix #25735
  • These changes do not require tests because there are no windows immersive mode tests
@highfive
Copy link

highfive commented Feb 27, 2020

Heads up! This PR modifies the following files:

  • @asajeffrey: components/script/dom/webglrenderingcontext.rs, components/script/dom/webglframebuffer.rs, components/script/dom/xrsession.rs
  • @KiChjang: components/script/dom/webglrenderingcontext.rs, components/script/dom/webglframebuffer.rs, components/script/dom/xrsession.rs
@highfive
Copy link

highfive commented Feb 27, 2020

warning Warning warning

  • These commits modify script code, but no tests are modified. Please consider adding a test!
@jdm jdm force-pushed the jdm:surface-inversion branch 3 times, most recently from 7eccb8c to 087866a Feb 27, 2020
@jdm
Copy link
Member Author

jdm commented Feb 28, 2020

The good: in my testing of https://threejs.org/examples/webxr_vr_paint.html and https://threejs.org/examples/webxr_vr_ballshooter.html (and other content from https://servo.org/hl-home/), these changes significantly improve the framerate in my Windows MR VR headset.

The bad: every single babylon demo on that page starts off looking great (yay!) then triggers some kind of D3D error that makes it disconnect the underlying device.

Copy link
Member

asajeffrey left a comment

Some minor comments, mosly LGTM. My main concern about this is that it overlaps quite a bit with surfmanup, so we'll need to think about how to stage them.

Cargo.toml Outdated
#webxr = { path = "../webxr/webxr" }
#webxr-api = { path = "../webxr/webxr-api" }
webxr = { git = "https://github.com/jdm/webxr", branch = "invert" }
webxr-api = { git = "https://github.com/jdm/webxr", branch = "invert" }

This comment has been minimized.

@asajeffrey

asajeffrey Feb 28, 2020

Member

Get rid of these once the respective PRs merge.

@@ -27,8 +28,10 @@ use webxr_api::SwapChainId as WebXRSwapChainId;
pub struct WebGLComm {
pub webgl_threads: WebGLThreads,
pub webxr_swap_chains: SwapChains<WebXRSwapChainId>,
pub webxr_surface_providers: SurfaceProviders,

This comment has been minimized.

@asajeffrey

asajeffrey Feb 28, 2020

Member

I'd prefer an API that merged these two back together, but that can wait for now.

This comment has been minimized.

@jdm

jdm Feb 28, 2020

Author Member

The swap chains and surface providers, you mean?

This comment has been minimized.

@asajeffrey

asajeffrey Feb 28, 2020

Member

Yes, I suspect that would be tidier, but it's not an urgent change.

pub image_handler: Box<dyn WebrenderExternalImageApi>,
pub output_handler: Option<Box<dyn webrender_api::OutputImageHandler>>,
pub webgl_executor: WebGlExecutor,

This comment has been minimized.

@asajeffrey

asajeffrey Feb 28, 2020

Member

Scary API for running unknown code! I'm confused where this is used.

This comment has been minimized.

@jdm

jdm Feb 28, 2020

Author Member

This facilitates this code which extracts the d3d11 device pointer from the surfman device on the webgl thread.

This comment has been minimized.

@asajeffrey

asajeffrey Feb 28, 2020

Member

OK, so as commented there if we could do this the other way round, and get the WebGL thread to use the OpenXR device, we could avoid this?

This comment has been minimized.

@jdm

jdm Feb 28, 2020

Author Member

The webgl thread is created at servo startup. The openxr device is created when a page requests an XR session.

This comment has been minimized.

@jdm

jdm Feb 28, 2020

Author Member

And I don't think we can't switch to using a different device later, because the page will have already created textures/framebuffers/etc. that won't be valid on a different device.

This comment has been minimized.

@asajeffrey

asajeffrey Feb 28, 2020

Member

Oh that's annoying! IIRC the spec does allow the GL context to be invalidated when the XR session starts, presumably for exactly this reason.

@jdm
Copy link
Member Author

jdm commented Feb 28, 2020

Manish tried this on a HL2 and the numbers were not as impressive as my VR testing suggested. Alan theorizes that the change from one d3d texture per eye to one d3d texture for both eyes ends up taking a slow path on HL2 but not on the VR device. It should be possible to investigate that theory by applying the same change to master and running it on both HL2 and the Samsung Odyssey+.

@jdm
Copy link
Member Author

jdm commented Mar 2, 2020

I applied the following diff to webxr in an otherwise-unmodified master build, using https://servo.org/hl-home/bbjs/#JA1ND3#164 as the testcase:

diff --git a/webxr/openxr/mod.rs b/webxr/openxr/mod.rs
index 54accd7..0d8df03 100644
--- a/webxr/openxr/mod.rs
+++ b/webxr/openxr/mod.rs
@@ -143,9 +143,9 @@ struct OpenXrDevice {
     left_swapchain: Swapchain<D3D11>,
     left_image: u32,
     left_images: Vec<<D3D11 as Graphics>::SwapchainImage>,
-    right_swapchain: Swapchain<D3D11>,
+    /*right_swapchain: Swapchain<D3D11>,
     right_image: u32,
-    right_images: Vec<<D3D11 as Graphics>::SwapchainImage>,
+    right_images: Vec<<D3D11 as Graphics>::SwapchainImage>,*/
     surfman: (SurfmanDevice, SurfmanContext),
 
     // input
@@ -284,8 +284,7 @@ impl OpenXrDevice {
             usage_flags: SwapchainUsageFlags::COLOR_ATTACHMENT | SwapchainUsageFlags::SAMPLED,
             format,
             sample_count: 1,
-            // XXXManishearth what if the recommended widths are different?
-            width: left_view_configuration.recommended_image_rect_width,
+            width: left_view_configuration.recommended_image_rect_width + right_view_configuration.recommended_image_rect_width,
             height: left_view_configuration.recommended_image_rect_height,
             face_count: 1,
             array_size: 1,
@@ -298,12 +297,12 @@ impl OpenXrDevice {
         let left_images = left_swapchain
             .enumerate_images()
             .map_err(|e| Error::BackendSpecific(format!("{:?}", e)))?;
-        let right_swapchain = session
+        /*let right_swapchain = session
             .create_swapchain(&swapchain_create_info)
             .map_err(|e| Error::BackendSpecific(format!("{:?}", e)))?;
         let right_images = right_swapchain
             .enumerate_images()
-            .map_err(|e| Error::BackendSpecific(format!("{:?}", e)))?;
+            .map_err(|e| Error::BackendSpecific(format!("{:?}", e)))?;*/
 
         // input
 
@@ -339,11 +338,11 @@ impl OpenXrDevice {
             blend_mode,
             view_configurations,
             left_swapchain,
-            right_swapchain,
+            //right_swapchain,
             left_images,
-            right_images,
+            //right_images,
             left_image: 0,
-            right_image: 0,
+            //right_image: 0,
             surfman: surfman.extract(),
 
             action_set,
@@ -533,19 +532,19 @@ impl DeviceAPI<Surface> for OpenXrDevice {
         self.left_swapchain
             .wait_image(openxr::Duration::INFINITE)
             .unwrap();
-        self.right_image = self.right_swapchain.acquire_image().unwrap();
+        /*self.right_image = self.right_swapchain.acquire_image().unwrap();
         self.right_swapchain
             .wait_image(openxr::Duration::INFINITE)
-            .unwrap();
+            .unwrap();*/
 
         let left_image = self.left_images[self.left_image as usize];
-        let right_image = self.right_images[self.right_image as usize];
+        //let right_image = self.right_images[self.right_image as usize];
 
         let left_surface = unsafe {
             device
                 .create_surface_from_texture(
                     &context,
-                    &Size2D::new(size.width / 2, size.height),
+                    &Size2D::new(size.width /*/ 2*/, size.height),
                     left_image,
                 )
                 .expect("couldn't create left surface")
@@ -555,7 +554,7 @@ impl DeviceAPI<Surface> for OpenXrDevice {
             .expect("couldn't create left surface texture");
         let left_texture_id = left_surface_texture.gl_texture();
 
-        let right_surface = unsafe {
+        /*let right_surface = unsafe {
             device
                 .create_surface_from_texture(
                     &context,
@@ -567,7 +566,7 @@ impl DeviceAPI<Surface> for OpenXrDevice {
         let right_surface_texture = device
             .create_surface_texture(context, right_surface)
             .expect("couldn't create right surface texture");
-        let right_texture_id = right_surface_texture.gl_texture();
+        let right_texture_id = right_surface_texture.gl_texture();*/
 
         self.gl
             .bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.write_fbo);
@@ -586,11 +585,11 @@ impl DeviceAPI<Surface> for OpenXrDevice {
         self.gl.blit_framebuffer(
             0,
             0,
-            size.width / 2,
+            size.width /*/ 2*/,
             size.height,
             0,
             size.height,
-            size.width / 2,
+            size.width /*/ 2*/,
             0,
             gl::COLOR_BUFFER_BIT,
             gl::NEAREST,
@@ -598,7 +597,7 @@ impl DeviceAPI<Surface> for OpenXrDevice {
         debug_assert_eq!(self.gl.get_error(), gl::NO_ERROR);
 
         // Bind the right eye's texture to the draw framebuffer.
-        self.gl.framebuffer_texture_2d(
+        /*self.gl.framebuffer_texture_2d(
             gl::DRAW_FRAMEBUFFER,
             gl::COLOR_ATTACHMENT0,
             device.surface_gl_texture_target(),
@@ -621,13 +620,13 @@ impl DeviceAPI<Surface> for OpenXrDevice {
         );
         debug_assert_eq!(self.gl.get_error(), gl::NO_ERROR);
 
-        self.gl.flush();
+        self.gl.flush();*/
 
         // Restore old GL bindings.
         self.gl.bind_framebuffer(gl::FRAMEBUFFER, old_framebuffer);
 
         self.left_swapchain.release_image().unwrap();
-        self.right_swapchain.release_image().unwrap();
+        //self.right_swapchain.release_image().unwrap();
         self.frame_stream
             .end(
                 self.frame_state.predicted_display_time,
@@ -654,10 +653,10 @@ impl DeviceAPI<Surface> for OpenXrDevice {
                             .fov(self.openxr_views[1].fov)
                             .sub_image(
                                 openxr::SwapchainSubImage::new()
-                                    .swapchain(&self.right_swapchain)
+                                    .swapchain(&self.left_swapchain)
                                     .image_array_index(0)
                                     .image_rect(openxr::Rect2Di {
-                                        offset: openxr::Offset2Di { x: 0, y: 0 },
+                                        offset: openxr::Offset2Di { x: self.left_extent.width, y: 0 },
                                         extent: self.right_extent,
                                     }),
                             ),
@@ -673,10 +672,10 @@ impl DeviceAPI<Surface> for OpenXrDevice {
             .unwrap();
         device.destroy_surface(context, left_surface).unwrap();
 
-        let right_surface = device
+        /*let right_surface = device
             .destroy_surface_texture(context, right_surface_texture)
             .unwrap();
-        device.destroy_surface(context, right_surface).unwrap();
+        device.destroy_surface(context, right_surface).unwrap();*/
 
         surface
     }

There was not a clear improvement in visual quality in the VR headset, and comparing against a build that avoided the GL->D3D blit there was a noticeable visual quality improvement in the latter. I have not yet had a chance to try this change in a HL2.

@jdm
Copy link
Member Author

jdm commented Mar 3, 2020

I can reproduce the results on HL2 where the performance difference is negligible. Profiling the paint demo immediately turns up:

  • webrender is taking up more than half of the the time that the main thread is running; we really need to finish off #25837 and this should disappear.
  • most of the time in the webgl thread is now spent under SwapChain::swap_buffers, and most of that is in Device::bind_surface_to_context where I suspect we end up taking the glFinish codepath (because the surfaces that wrap textures don't have keyed mutexes)
    • I recall asking about keyed mutexes and the textures in the past, and we may be able to make use of fences instead
@jdm
Copy link
Member Author

jdm commented Mar 3, 2020

With #25837 applied, the paint demo FPS starts at 35 and eventually consistently reaches 50. It's not totally clear why there's a ramp up period; it would be worth looking at a profile that spans the full time period and comparing the stacks in the first 5-10 seconds versus the last 5-10 seconds.

@jdm
Copy link
Member Author

jdm commented Mar 3, 2020

With https://github.com/pcwalton/surfman/blob/ae14cc9afd140bc73e89f3fe8c080f729f09ccd2/surfman/src/platform/windows/angle/context.rs#L272 changed to use Flush instead, the FPS never drops below 45 at any point and usually sits at 50 \o/

@asajeffrey
Copy link
Member

asajeffrey commented Mar 3, 2020

The relevant spec discussion is https://github.com/immersive-web/webxr/issues/968

@asajeffrey
Copy link
Member

asajeffrey commented Mar 3, 2020

It's not obvious to me why Finish() or Flush() are needed there, I can imagine needing them for unbind (to make sure that any pending drawing on the surface has happened) but not bind.

@jdm
Copy link
Member Author

jdm commented Mar 3, 2020

The low framerate babylon demos only went up by 1 or 2 FPS with the finish->flush change, and profiles show the vast majority of time being spent in the script thread, unsurprisingly.

@jdm
Copy link
Member Author

jdm commented Mar 4, 2020

@Manishearth This branch should be full ready for testing.

@bors-servo
Copy link
Contributor

bors-servo commented Mar 5, 2020

The latest upstream changes (presumably #25863) made this pull request unmergeable. Please resolve the merge conflicts.

@jdm jdm force-pushed the jdm:surface-inversion branch 2 times, most recently from bccc1d5 to 5a9ae36 Mar 5, 2020
@jdm
Copy link
Member Author

jdm commented Mar 6, 2020

@bors-servo r=asajeffrey,manishearth

@bors-servo
Copy link
Contributor

bors-servo commented Mar 6, 2020

📌 Commit 5a9ae36 has been approved by asajeffrey,manishearth

@bors-servo
Copy link
Contributor

bors-servo commented Mar 9, 2020

💔 Test failed - status-taskcluster

@jdm
Copy link
Member Author

jdm commented Mar 9, 2020

@jdm
Copy link
Member Author

jdm commented Mar 9, 2020

@bors-servo r-
Too many timeouts on mac webgl tests for comfort.

@jdm jdm force-pushed the jdm:surface-inversion branch from 162db37 to fbcf2bb Mar 9, 2020
@jdm
Copy link
Member Author

jdm commented Mar 9, 2020

@bors-servo r=Manishearth,asajeffrey

@bors-servo
Copy link
Contributor

bors-servo commented Mar 9, 2020

📌 Commit fbcf2bb has been approved by Manishearth,asajeffrey

@jdm
Copy link
Member Author

jdm commented Mar 9, 2020

@bors-servo try=wpt

@bors-servo
Copy link
Contributor

bors-servo commented Mar 9, 2020

Trying commit fbcf2bb with merge 5dbea4c...

bors-servo added a commit that referenced this pull request Mar 9, 2020
Remove GL->d3d blit in HoloLens immersive mode

Depends on:
* servo/surfman#151
* asajeffrey/surfman-chains#7
* servo/webxr#133

These changes add two extra APIs for embedders to use when registering a WebXR device - one to allow running any closure as a task in the webgl thread, and one to register an arbitrary surface provider for a particular webxr session. When an openxr session is started, it can then obtain the webgl thread's d3d device from that thread's surfman device and ensure that openxr uses it.

Surface providers are traits that have their methods invoked by the webgl thread as part of the the normal swapchain operations. This allows the openxr surface provider to return surfaces that wrap the underlying openxr textures, which are valid in the webgl thread and can be used as the target of an opaque framebuffer.

---
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #25735
- [x] These changes do not require tests because there are no windows immersive mode tests
@bors-servo
Copy link
Contributor

bors-servo commented Mar 9, 2020

☀️ Test successful - status-taskcluster
State: approved=Manishearth,asajeffrey try=True

@bors-servo
Copy link
Contributor

bors-servo commented Mar 9, 2020

Testing commit fbcf2bb with merge 2d30e27...

bors-servo added a commit that referenced this pull request Mar 9, 2020
Remove GL->d3d blit in HoloLens immersive mode

Depends on:
* servo/surfman#151
* asajeffrey/surfman-chains#7
* servo/webxr#133

These changes add two extra APIs for embedders to use when registering a WebXR device - one to allow running any closure as a task in the webgl thread, and one to register an arbitrary surface provider for a particular webxr session. When an openxr session is started, it can then obtain the webgl thread's d3d device from that thread's surfman device and ensure that openxr uses it.

Surface providers are traits that have their methods invoked by the webgl thread as part of the the normal swapchain operations. This allows the openxr surface provider to return surfaces that wrap the underlying openxr textures, which are valid in the webgl thread and can be used as the target of an opaque framebuffer.

---
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #25735
- [x] These changes do not require tests because there are no windows immersive mode tests
@Manishearth
Copy link
Member

Manishearth commented Mar 9, 2020

@bors-servo try-

i feel like we fixed the sticky try bug, but either way...

@jdm
Copy link
Member Author

jdm commented Mar 9, 2020

@bors-servo retry

@bors-servo
Copy link
Contributor

bors-servo commented Mar 9, 2020

Testing commit fbcf2bb with merge b4d7ec1...

@bors-servo
Copy link
Contributor

bors-servo commented Mar 10, 2020

☀️ Test successful - status-taskcluster
Approved by: Manishearth,asajeffrey
Pushing b4d7ec1 to master...

@bors-servo bors-servo merged commit b4d7ec1 into servo:master Mar 10, 2020
2 checks passed
2 checks passed
Community-TC (pull_request) TaskGroup: success
Details
homu Test successful
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

6 participants
You can’t perform that action at this time.