mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
webgpu: Introduce PresentationId to ensure updates with newer presentation (#33613)
* Introduce PresentationId to ensure update with newer presentation Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * Update swapchain.rs Signed-off-by: Samson <16504129+sagudev@users.noreply.github.com> --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> Signed-off-by: Samson <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
e1eca71cd1
commit
6e043cd09e
1 changed files with 48 additions and 31 deletions
|
@ -41,6 +41,11 @@ impl MallocSizeOf for WebGPUContextId {
|
||||||
|
|
||||||
pub type WGPUImageMap = Arc<Mutex<HashMap<WebGPUContextId, ContextData>>>;
|
pub type WGPUImageMap = Arc<Mutex<HashMap<WebGPUContextId, ContextData>>>;
|
||||||
|
|
||||||
|
/// Presentation id encodes current configuration and current image
|
||||||
|
/// so that async presentation does not update context with older data
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
struct PresentationId(u64);
|
||||||
|
|
||||||
struct GPUPresentationBuffer {
|
struct GPUPresentationBuffer {
|
||||||
global: Arc<Global>,
|
global: Arc<Global>,
|
||||||
buffer_id: id::BufferId,
|
buffer_id: id::BufferId,
|
||||||
|
@ -188,6 +193,12 @@ pub struct ContextData {
|
||||||
buffer_ids: ArrayVec<(id::BufferId, PresentationBufferState), PRESENTATION_BUFFER_COUNT>,
|
buffer_ids: ArrayVec<(id::BufferId, PresentationBufferState), PRESENTATION_BUFFER_COUNT>,
|
||||||
/// If there is no associated swapchain the context is dummy (transparent black)
|
/// If there is no associated swapchain the context is dummy (transparent black)
|
||||||
swap_chain: Option<SwapChain>,
|
swap_chain: Option<SwapChain>,
|
||||||
|
/// Next presentation id to be returned
|
||||||
|
next_presentation_id: PresentationId,
|
||||||
|
/// Current id that is presented/configured
|
||||||
|
///
|
||||||
|
/// This value only grows
|
||||||
|
current_presentation_id: PresentationId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextData {
|
impl ContextData {
|
||||||
|
@ -213,6 +224,8 @@ impl ContextData {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&buffer_id| (buffer_id, PresentationBufferState::Unassigned))
|
.map(|&buffer_id| (buffer_id, PresentationBufferState::Unassigned))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
current_presentation_id: PresentationId(0),
|
||||||
|
next_presentation_id: PresentationId(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,6 +264,7 @@ impl ContextData {
|
||||||
);
|
);
|
||||||
Some(buffer_id)
|
Some(buffer_id)
|
||||||
} else {
|
} else {
|
||||||
|
error!("No available presentation buffer: {:?}", self.buffer_ids);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,6 +322,23 @@ impl ContextData {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.send_transaction(webrender_document, txn);
|
.send_transaction(webrender_document, txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if presentation id was updated (was newer)
|
||||||
|
fn check_and_update_presentation_id(&mut self, presentation_id: PresentationId) -> bool {
|
||||||
|
if presentation_id > self.current_presentation_id {
|
||||||
|
self.current_presentation_id = presentation_id;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new presentation id
|
||||||
|
fn next_presentation_id(&mut self) -> PresentationId {
|
||||||
|
let res = PresentationId(self.next_presentation_id.0);
|
||||||
|
self.next_presentation_id.0 += 1;
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::WGPU {
|
impl crate::WGPU {
|
||||||
|
@ -349,6 +380,9 @@ impl crate::WGPU {
|
||||||
let mut webgpu_contexts = self.wgpu_image_map.lock().unwrap();
|
let mut webgpu_contexts = self.wgpu_image_map.lock().unwrap();
|
||||||
let context_data = webgpu_contexts.get_mut(&context_id).unwrap();
|
let context_data = webgpu_contexts.get_mut(&context_id).unwrap();
|
||||||
|
|
||||||
|
let presentation_id = context_data.next_presentation_id();
|
||||||
|
context_data.check_and_update_presentation_id(presentation_id);
|
||||||
|
|
||||||
// If configuration is not provided or presentation format is not valid
|
// If configuration is not provided or presentation format is not valid
|
||||||
// the context will be dummy until recreation
|
// the context will be dummy until recreation
|
||||||
let format = config
|
let format = config
|
||||||
|
@ -416,6 +450,7 @@ impl crate::WGPU {
|
||||||
let queue_id;
|
let queue_id;
|
||||||
let buffer_id;
|
let buffer_id;
|
||||||
let image_desc;
|
let image_desc;
|
||||||
|
let presentation_id;
|
||||||
{
|
{
|
||||||
if let Some(context_data) = self.wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
if let Some(context_data) = self.wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
||||||
let Some(swap_chain) = context_data.swap_chain.as_ref() else {
|
let Some(swap_chain) = context_data.swap_chain.as_ref() else {
|
||||||
|
@ -425,6 +460,7 @@ impl crate::WGPU {
|
||||||
queue_id = swap_chain.queue_id;
|
queue_id = swap_chain.queue_id;
|
||||||
buffer_id = context_data.get_available_buffer(global).unwrap();
|
buffer_id = context_data.get_available_buffer(global).unwrap();
|
||||||
image_desc = context_data.image_desc;
|
image_desc = context_data.image_desc;
|
||||||
|
presentation_id = context_data.next_presentation_id();
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -484,6 +520,7 @@ impl crate::WGPU {
|
||||||
webrender_api,
|
webrender_api,
|
||||||
webrender_document,
|
webrender_document,
|
||||||
image_desc,
|
image_desc,
|
||||||
|
presentation_id,
|
||||||
);
|
);
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
@ -520,43 +557,23 @@ fn update_wr_image(
|
||||||
webrender_api: Arc<Mutex<RenderApi>>,
|
webrender_api: Arc<Mutex<RenderApi>>,
|
||||||
webrender_document: webrender_api::DocumentId,
|
webrender_document: webrender_api::DocumentId,
|
||||||
image_desc: WebGPUImageDescriptor,
|
image_desc: WebGPUImageDescriptor,
|
||||||
|
presentation_id: PresentationId,
|
||||||
) {
|
) {
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
if let Some(context_data) = wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
if let Some(context_data) = wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
||||||
let config_changed = image_desc != context_data.image_desc;
|
if !context_data.check_and_update_presentation_id(presentation_id) {
|
||||||
let buffer_state = context_data.get_buffer_state(buffer_id);
|
let buffer_state = context_data.get_buffer_state(buffer_id);
|
||||||
match buffer_state {
|
if *buffer_state == PresentationBufferState::Mapping {
|
||||||
PresentationBufferState::Unassigned => {
|
let _ = global.buffer_unmap(buffer_id);
|
||||||
// throw away all work, because we are from old swapchain
|
*buffer_state = PresentationBufferState::Available;
|
||||||
return;
|
}
|
||||||
},
|
// throw away all work, because we are too old
|
||||||
PresentationBufferState::Mapping => {},
|
|
||||||
_ => panic!("Unexpected presentation buffer state"),
|
|
||||||
}
|
|
||||||
if config_changed {
|
|
||||||
/*
|
|
||||||
This means that while mapasync was running, context got recreated
|
|
||||||
so we need to throw all out work away.
|
|
||||||
|
|
||||||
It is also possible that we got recreated with same config,
|
|
||||||
so canvas should be cleared, but we handle such case in gpucanvascontext
|
|
||||||
with drawing_buffer.cleared
|
|
||||||
|
|
||||||
One more case is that we already have newer map async done,
|
|
||||||
so we can replace new image with old image but that should happen very rarely
|
|
||||||
|
|
||||||
One possible solution to all problems is blocking device timeline
|
|
||||||
(wgpu thread or introduce new timeline/thread for presentation)
|
|
||||||
something like this is also mentioned in spec:
|
|
||||||
|
|
||||||
2. Ensure that all submitted work items (e.g. queue submissions) have completed writing to the image
|
|
||||||
https://gpuweb.github.io/gpuweb/#abstract-opdef-get-a-copy-of-the-image-contents-of-a-context
|
|
||||||
*/
|
|
||||||
let _ = global.buffer_unmap(buffer_id);
|
|
||||||
*buffer_state = PresentationBufferState::Available;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
assert_eq!(image_desc, context_data.image_desc);
|
||||||
|
let buffer_state = context_data.get_buffer_state(buffer_id);
|
||||||
|
assert_eq!(*buffer_state, PresentationBufferState::Mapping);
|
||||||
*buffer_state = PresentationBufferState::Mapped;
|
*buffer_state = PresentationBufferState::Mapped;
|
||||||
let presentation_buffer =
|
let presentation_buffer =
|
||||||
GPUPresentationBuffer::new(global, buffer_id, image_desc.buffer_size());
|
GPUPresentationBuffer::new(global, buffer_id, image_desc.buffer_size());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue