Skip to content

Commit c745863

Browse files
[hal/vk] Rework Submission and Surface Synchronization (#5681)
Fix two major synchronization issues in `wgpu_val::vulkan`: - Properly order queue command buffer submissions. Due to Mesa bugs, two semaphores are required even though the Vulkan spec says that only one should be necessary. - Properly manage surface texture acquisition and presentation: - Acquiring a surface texture can return while the presentation engine is still displaying the texture. Applications must wait for a semaphore to be signaled before using the acquired texture. - Presenting a surface texture requires a semaphore to ensure that drawing is complete before presentation occurs. Co-authored-by: Jim Blandy <[email protected]>
1 parent 9b7a965 commit c745863

File tree

18 files changed

+699
-304
lines changed

18 files changed

+699
-304
lines changed

wgpu-core/src/device/queue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,7 @@ impl Global {
14991499
.raw
15001500
.as_ref()
15011501
.unwrap()
1502-
.submit(&refs, &submit_surface_textures, Some((fence, submit_index)))
1502+
.submit(&refs, &submit_surface_textures, (fence, submit_index))
15031503
.map_err(DeviceError::from)?;
15041504
}
15051505

wgpu-core/src/present.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,20 @@ impl Global {
154154
parent_id: surface_id,
155155
});
156156
}
157-
#[cfg(not(feature = "trace"))]
158-
let _ = device;
157+
158+
let fence_guard = device.fence.read();
159+
let fence = fence_guard.as_ref().unwrap();
159160

160161
let suf = A::surface_as_hal(surface.as_ref());
161162
let (texture_id, status) = match unsafe {
162-
suf.unwrap()
163-
.acquire_texture(Some(std::time::Duration::from_millis(
164-
FRAME_TIMEOUT_MS as u64,
165-
)))
163+
suf.unwrap().acquire_texture(
164+
Some(std::time::Duration::from_millis(FRAME_TIMEOUT_MS as u64)),
165+
fence,
166+
)
166167
} {
167168
Ok(Some(ast)) => {
169+
drop(fence_guard);
170+
168171
let texture_desc = wgt::TextureDescriptor {
169172
label: (),
170173
size: wgt::Extent3d {

wgpu-hal/examples/halmark/main.rs

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ const MAX_BUNNIES: usize = 1 << 20;
2222
const BUNNY_SIZE: f32 = 0.15 * 256.0;
2323
const GRAVITY: f32 = -9.8 * 100.0;
2424
const MAX_VELOCITY: f32 = 750.0;
25-
const COMMAND_BUFFER_PER_CONTEXT: usize = 100;
2625
const DESIRED_MAX_LATENCY: u32 = 2;
2726

2827
#[repr(C)]
@@ -498,7 +497,7 @@ impl<A: hal::Api> Example<A> {
498497
let mut fence = device.create_fence().unwrap();
499498
let init_cmd = cmd_encoder.end_encoding().unwrap();
500499
queue
501-
.submit(&[&init_cmd], &[], Some((&mut fence, init_fence_value)))
500+
.submit(&[&init_cmd], &[], (&mut fence, init_fence_value))
502501
.unwrap();
503502
device.wait(&fence, init_fence_value, !0).unwrap();
504503
device.destroy_buffer(staging_buffer);
@@ -550,7 +549,7 @@ impl<A: hal::Api> Example<A> {
550549
{
551550
let ctx = &mut self.contexts[self.context_index];
552551
self.queue
553-
.submit(&[], &[], Some((&mut ctx.fence, ctx.fence_value)))
552+
.submit(&[], &[], (&mut ctx.fence, ctx.fence_value))
554553
.unwrap();
555554
}
556555

@@ -650,7 +649,13 @@ impl<A: hal::Api> Example<A> {
650649

651650
let ctx = &mut self.contexts[self.context_index];
652651

653-
let surface_tex = unsafe { self.surface.acquire_texture(None).unwrap().unwrap().texture };
652+
let surface_tex = unsafe {
653+
self.surface
654+
.acquire_texture(None, &ctx.fence)
655+
.unwrap()
656+
.unwrap()
657+
.texture
658+
};
654659

655660
let target_barrier0 = hal::TextureBarrier {
656661
texture: surface_tex.borrow(),
@@ -718,7 +723,6 @@ impl<A: hal::Api> Example<A> {
718723
}
719724

720725
ctx.frames_recorded += 1;
721-
let do_fence = ctx.frames_recorded > COMMAND_BUFFER_PER_CONTEXT;
722726

723727
let target_barrier1 = hal::TextureBarrier {
724728
texture: surface_tex.borrow(),
@@ -732,45 +736,42 @@ impl<A: hal::Api> Example<A> {
732736

733737
unsafe {
734738
let cmd_buf = ctx.encoder.end_encoding().unwrap();
735-
let fence_param = if do_fence {
736-
Some((&mut ctx.fence, ctx.fence_value))
737-
} else {
738-
None
739-
};
740739
self.queue
741-
.submit(&[&cmd_buf], &[&surface_tex], fence_param)
740+
.submit(
741+
&[&cmd_buf],
742+
&[&surface_tex],
743+
(&mut ctx.fence, ctx.fence_value),
744+
)
742745
.unwrap();
743746
self.queue.present(&self.surface, surface_tex).unwrap();
744747
ctx.used_cmd_bufs.push(cmd_buf);
745748
ctx.used_views.push(surface_tex_view);
746749
};
747750

748-
if do_fence {
749-
log::debug!("Context switch from {}", self.context_index);
750-
let old_fence_value = ctx.fence_value;
751-
if self.contexts.len() == 1 {
752-
let hal_desc = hal::CommandEncoderDescriptor {
753-
label: None,
754-
queue: &self.queue,
755-
};
756-
self.contexts.push(unsafe {
757-
ExecutionContext {
758-
encoder: self.device.create_command_encoder(&hal_desc).unwrap(),
759-
fence: self.device.create_fence().unwrap(),
760-
fence_value: 0,
761-
used_views: Vec::new(),
762-
used_cmd_bufs: Vec::new(),
763-
frames_recorded: 0,
764-
}
765-
});
766-
}
767-
self.context_index = (self.context_index + 1) % self.contexts.len();
768-
let next = &mut self.contexts[self.context_index];
769-
unsafe {
770-
next.wait_and_clear(&self.device);
771-
}
772-
next.fence_value = old_fence_value + 1;
751+
log::debug!("Context switch from {}", self.context_index);
752+
let old_fence_value = ctx.fence_value;
753+
if self.contexts.len() == 1 {
754+
let hal_desc = hal::CommandEncoderDescriptor {
755+
label: None,
756+
queue: &self.queue,
757+
};
758+
self.contexts.push(unsafe {
759+
ExecutionContext {
760+
encoder: self.device.create_command_encoder(&hal_desc).unwrap(),
761+
fence: self.device.create_fence().unwrap(),
762+
fence_value: 0,
763+
used_views: Vec::new(),
764+
used_cmd_bufs: Vec::new(),
765+
frames_recorded: 0,
766+
}
767+
});
768+
}
769+
self.context_index = (self.context_index + 1) % self.contexts.len();
770+
let next = &mut self.contexts[self.context_index];
771+
unsafe {
772+
next.wait_and_clear(&self.device);
773773
}
774+
next.fence_value = old_fence_value + 1;
774775
}
775776
}
776777

wgpu-hal/examples/raw-gles.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
156156
})
157157
.unwrap()
158158
};
159+
let mut fence = unsafe { od.device.create_fence().unwrap() };
159160
let rp_desc = hal::RenderPassDescriptor {
160161
label: None,
161162
extent: wgt::Extent3d {
@@ -183,6 +184,6 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
183184
encoder.begin_render_pass(&rp_desc);
184185
encoder.end_render_pass();
185186
let cmd_buf = encoder.end_encoding().unwrap();
186-
od.queue.submit(&[&cmd_buf], &[], None).unwrap();
187+
od.queue.submit(&[&cmd_buf], &[], (&mut fence, 0)).unwrap();
187188
}
188189
}

wgpu-hal/examples/ray-traced-triangle/main.rs

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use std::{
1313
};
1414
use winit::window::WindowButtons;
1515

16-
const COMMAND_BUFFER_PER_CONTEXT: usize = 100;
1716
const DESIRED_MAX_LATENCY: u32 = 2;
1817

1918
/// [D3D12_RAYTRACING_INSTANCE_DESC](https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#d3d12_raytracing_instance_desc)
@@ -759,7 +758,7 @@ impl<A: hal::Api> Example<A> {
759758
let mut fence = device.create_fence().unwrap();
760759
let init_cmd = cmd_encoder.end_encoding().unwrap();
761760
queue
762-
.submit(&[&init_cmd], &[], Some((&mut fence, init_fence_value)))
761+
.submit(&[&init_cmd], &[], (&mut fence, init_fence_value))
763762
.unwrap();
764763
device.wait(&fence, init_fence_value, !0).unwrap();
765764
cmd_encoder.reset_all(iter::once(init_cmd));
@@ -808,7 +807,13 @@ impl<A: hal::Api> Example<A> {
808807
fn render(&mut self) {
809808
let ctx = &mut self.contexts[self.context_index];
810809

811-
let surface_tex = unsafe { self.surface.acquire_texture(None).unwrap().unwrap().texture };
810+
let surface_tex = unsafe {
811+
self.surface
812+
.acquire_texture(None, &ctx.fence)
813+
.unwrap()
814+
.unwrap()
815+
.texture
816+
};
812817

813818
let target_barrier0 = hal::TextureBarrier {
814819
texture: surface_tex.borrow(),
@@ -909,7 +914,6 @@ impl<A: hal::Api> Example<A> {
909914
}
910915

911916
ctx.frames_recorded += 1;
912-
let do_fence = ctx.frames_recorded > COMMAND_BUFFER_PER_CONTEXT;
913917

914918
let target_barrier1 = hal::TextureBarrier {
915919
texture: surface_tex.borrow(),
@@ -959,53 +963,50 @@ impl<A: hal::Api> Example<A> {
959963

960964
unsafe {
961965
let cmd_buf = ctx.encoder.end_encoding().unwrap();
962-
let fence_param = if do_fence {
963-
Some((&mut ctx.fence, ctx.fence_value))
964-
} else {
965-
None
966-
};
967966
self.queue
968-
.submit(&[&cmd_buf], &[&surface_tex], fence_param)
967+
.submit(
968+
&[&cmd_buf],
969+
&[&surface_tex],
970+
(&mut ctx.fence, ctx.fence_value),
971+
)
969972
.unwrap();
970973
self.queue.present(&self.surface, surface_tex).unwrap();
971974
ctx.used_cmd_bufs.push(cmd_buf);
972975
ctx.used_views.push(surface_tex_view);
973976
};
974977

975-
if do_fence {
976-
log::info!("Context switch from {}", self.context_index);
977-
let old_fence_value = ctx.fence_value;
978-
if self.contexts.len() == 1 {
979-
let hal_desc = hal::CommandEncoderDescriptor {
980-
label: None,
981-
queue: &self.queue,
982-
};
983-
self.contexts.push(unsafe {
984-
ExecutionContext {
985-
encoder: self.device.create_command_encoder(&hal_desc).unwrap(),
986-
fence: self.device.create_fence().unwrap(),
987-
fence_value: 0,
988-
used_views: Vec::new(),
989-
used_cmd_bufs: Vec::new(),
990-
frames_recorded: 0,
991-
}
992-
});
993-
}
994-
self.context_index = (self.context_index + 1) % self.contexts.len();
995-
let next = &mut self.contexts[self.context_index];
996-
unsafe {
997-
next.wait_and_clear(&self.device);
998-
}
999-
next.fence_value = old_fence_value + 1;
978+
log::info!("Context switch from {}", self.context_index);
979+
let old_fence_value = ctx.fence_value;
980+
if self.contexts.len() == 1 {
981+
let hal_desc = hal::CommandEncoderDescriptor {
982+
label: None,
983+
queue: &self.queue,
984+
};
985+
self.contexts.push(unsafe {
986+
ExecutionContext {
987+
encoder: self.device.create_command_encoder(&hal_desc).unwrap(),
988+
fence: self.device.create_fence().unwrap(),
989+
fence_value: 0,
990+
used_views: Vec::new(),
991+
used_cmd_bufs: Vec::new(),
992+
frames_recorded: 0,
993+
}
994+
});
995+
}
996+
self.context_index = (self.context_index + 1) % self.contexts.len();
997+
let next = &mut self.contexts[self.context_index];
998+
unsafe {
999+
next.wait_and_clear(&self.device);
10001000
}
1001+
next.fence_value = old_fence_value + 1;
10011002
}
10021003

10031004
fn exit(mut self) {
10041005
unsafe {
10051006
{
10061007
let ctx = &mut self.contexts[self.context_index];
10071008
self.queue
1008-
.submit(&[], &[], Some((&mut ctx.fence, ctx.fence_value)))
1009+
.submit(&[], &[], (&mut ctx.fence, ctx.fence_value))
10091010
.unwrap();
10101011
}
10111012

wgpu-hal/src/dx12/mod.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@ impl crate::Surface for Surface {
857857
unsafe fn acquire_texture(
858858
&self,
859859
timeout: Option<std::time::Duration>,
860+
_fence: &Fence,
860861
) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
861862
let mut swapchain = self.swap_chain.write();
862863
let sc = swapchain.as_mut().unwrap();
@@ -895,7 +896,7 @@ impl crate::Queue for Queue {
895896
&self,
896897
command_buffers: &[&CommandBuffer],
897898
_surface_textures: &[&Texture],
898-
signal_fence: Option<(&mut Fence, crate::FenceValue)>,
899+
(signal_fence, signal_value): (&mut Fence, crate::FenceValue),
899900
) -> Result<(), crate::DeviceError> {
900901
let mut temp_lists = self.temp_lists.lock();
901902
temp_lists.clear();
@@ -908,11 +909,9 @@ impl crate::Queue for Queue {
908909
self.raw.execute_command_lists(&temp_lists);
909910
}
910911

911-
if let Some((fence, value)) = signal_fence {
912-
self.raw
913-
.signal(&fence.raw, value)
914-
.into_device_result("Signal fence")?;
915-
}
912+
self.raw
913+
.signal(&signal_fence.raw, signal_value)
914+
.into_device_result("Signal fence")?;
916915

917916
// Note the lack of synchronization here between the main Direct queue
918917
// and the dedicated presentation queue. This is automatically handled

wgpu-hal/src/empty.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ impl crate::Surface for Context {
7575
unsafe fn acquire_texture(
7676
&self,
7777
timeout: Option<std::time::Duration>,
78+
fence: &Resource,
7879
) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
7980
Ok(None)
8081
}
@@ -114,7 +115,7 @@ impl crate::Queue for Context {
114115
&self,
115116
command_buffers: &[&Resource],
116117
surface_textures: &[&Resource],
117-
signal_fence: Option<(&mut Resource, crate::FenceValue)>,
118+
signal_fence: (&mut Resource, crate::FenceValue),
118119
) -> DeviceResult<()> {
119120
Ok(())
120121
}

wgpu-hal/src/gles/egl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,7 @@ impl crate::Surface for Surface {
14321432
unsafe fn acquire_texture(
14331433
&self,
14341434
_timeout_ms: Option<Duration>, //TODO
1435+
_fence: &super::Fence,
14351436
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
14361437
let swapchain = self.swapchain.read();
14371438
let sc = swapchain.as_ref().unwrap();

wgpu-hal/src/gles/queue.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,7 +1740,7 @@ impl crate::Queue for super::Queue {
17401740
&self,
17411741
command_buffers: &[&super::CommandBuffer],
17421742
_surface_textures: &[&super::Texture],
1743-
signal_fence: Option<(&mut super::Fence, crate::FenceValue)>,
1743+
(signal_fence, signal_value): (&mut super::Fence, crate::FenceValue),
17441744
) -> Result<(), crate::DeviceError> {
17451745
let shared = Arc::clone(&self.shared);
17461746
let gl = &shared.context.lock();
@@ -1774,12 +1774,10 @@ impl crate::Queue for super::Queue {
17741774
}
17751775
}
17761776

1777-
if let Some((fence, value)) = signal_fence {
1778-
fence.maintain(gl);
1779-
let sync = unsafe { gl.fence_sync(glow::SYNC_GPU_COMMANDS_COMPLETE, 0) }
1780-
.map_err(|_| crate::DeviceError::OutOfMemory)?;
1781-
fence.pending.push((value, sync));
1782-
}
1777+
signal_fence.maintain(gl);
1778+
let sync = unsafe { gl.fence_sync(glow::SYNC_GPU_COMMANDS_COMPLETE, 0) }
1779+
.map_err(|_| crate::DeviceError::OutOfMemory)?;
1780+
signal_fence.pending.push((signal_value, sync));
17831781

17841782
Ok(())
17851783
}

wgpu-hal/src/gles/web.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ impl crate::Surface for Surface {
427427
unsafe fn acquire_texture(
428428
&self,
429429
_timeout_ms: Option<std::time::Duration>, //TODO
430+
_fence: &super::Fence,
430431
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
431432
let swapchain = self.swapchain.read();
432433
let sc = swapchain.as_ref().unwrap();

wgpu-hal/src/gles/wgl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ impl crate::Surface for Surface {
798798
unsafe fn acquire_texture(
799799
&self,
800800
_timeout_ms: Option<Duration>,
801+
_fence: &super::Fence,
801802
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
802803
let swapchain = self.swapchain.read();
803804
let sc = swapchain.as_ref().unwrap();

0 commit comments

Comments
 (0)