Skip to content

Commit 8dd7f0e

Browse files
committed
implement wgpuInstanceWaitAny and other futures logic
1 parent 79f48e6 commit 8dd7f0e

File tree

3 files changed

+176
-21
lines changed

3 files changed

+176
-21
lines changed

src/lib.rs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use std::{
2121
thread,
2222
};
2323
use utils::{
24-
get_base_device_limits_from_adapter_limits, make_slice, str_into_string_view,
25-
string_view_into_label, string_view_into_str, texture_format_has_depth,
24+
get_base_device_limits_from_adapter_limits, make_slice, make_slice_mut, str_into_string_view,
25+
string_view_into_label, string_view_into_str, texture_format_has_depth, FutureRegistry,
2626
};
2727
use wgc::{
2828
command::{bundle_ffi, ComputePass, RenderPass},
@@ -46,6 +46,7 @@ pub mod native {
4646

4747
pub struct Context {
4848
global: wgc::global::Global,
49+
futures: parking_lot::RwLock<FutureRegistry>,
4950
}
5051

5152
pub struct WGPUAdapterImpl {
@@ -673,6 +674,7 @@ pub unsafe extern "C" fn wgpuCreateInstance(
673674
Arc::into_raw(Arc::new(WGPUInstanceImpl {
674675
context: Arc::new(Context {
675676
global: wgc::global::Global::new("wgpu", &instance_desc),
677+
futures: Default::default(),
676678
}),
677679
}))
678680
}
@@ -865,7 +867,8 @@ pub unsafe extern "C" fn wgpuAdapterRequestDevice(
865867
}
866868
};
867869

868-
NULL_FUTURE
870+
// `context.global.adapter_request_device` resolves immediately
871+
context.futures.write().completed_future().into()
869872
}
870873

871874
#[no_mangle]
@@ -998,6 +1001,9 @@ pub unsafe extern "C" fn wgpuBufferMapAsync(
9981001
let callback = callback_info.callback.expect("invalid callback");
9991002
let userdata = new_userdata!(callback_info);
10001003

1004+
let id = context.futures.write().incomplete_future();
1005+
let dup_id = id.clone();
1006+
let ctx = context.clone();
10011007
let operation = wgc::resource::BufferMapOperation {
10021008
host: match mode as native::WGPUMapMode {
10031009
native::WGPUMapMode_Write => wgc::device::HostMap::Write,
@@ -1025,6 +1031,7 @@ pub unsafe extern "C" fn wgpuBufferMapAsync(
10251031
userdata.get_1(),
10261032
userdata.get_2(),
10271033
);
1034+
ctx.futures.write().complete(dup_id);
10281035
})),
10291036
};
10301037

@@ -1037,8 +1044,7 @@ pub unsafe extern "C" fn wgpuBufferMapAsync(
10371044
handle_error(error_sink, cause, None, "wgpuBufferMapAsync");
10381045
};
10391046

1040-
// TODO: Properly handle futures.
1041-
NULL_FUTURE
1047+
id.into()
10421048
}
10431049

10441050
#[no_mangle]
@@ -2637,7 +2643,7 @@ pub unsafe extern "C" fn wgpuDevicePopErrorScope(
26372643
}
26382644
};
26392645

2640-
NULL_FUTURE
2646+
device.context.futures.write().completed_future().into()
26412647
}
26422648

26432649
#[no_mangle]
@@ -2746,6 +2752,7 @@ pub unsafe extern "C" fn wgpuInstanceRequestAdapter(
27462752
let context = &instance.context;
27472753
let callback = callback_info.callback.expect("invalid callback");
27482754

2755+
let id = context.futures.write().completed_future();
27492756
let (desc, inputs) = match options {
27502757
Some(options) => (
27512758
wgt::RequestAdapterOptions {
@@ -2776,7 +2783,7 @@ pub unsafe extern "C" fn wgpuInstanceRequestAdapter(
27762783
callback_info.userdata1,
27772784
callback_info.userdata2,
27782785
);
2779-
return NULL_FUTURE;
2786+
return id.into();
27802787
}
27812788
backend_type => panic!("invalid backend type: 0x{backend_type:08X}"),
27822789
},
@@ -2819,7 +2826,7 @@ pub unsafe extern "C" fn wgpuInstanceRequestAdapter(
28192826
}
28202827
};
28212828

2822-
NULL_FUTURE
2829+
id.into()
28232830
}
28242831

28252832
#[no_mangle]
@@ -2940,20 +2947,23 @@ pub unsafe extern "C" fn wgpuQueueOnSubmittedWorkDone(
29402947
let callback = callback_info.callback.expect("invalid callback");
29412948
let userdata = new_userdata!(callback_info);
29422949

2950+
let id = context.futures.write().incomplete_future();
2951+
let dup_id = id.clone();
2952+
let ctx = context.clone();
29432953
let closure: wgc::device::queue::SubmittedWorkDoneClosure = Box::new(move || {
29442954
callback(
29452955
native::WGPUQueueWorkDoneStatus_Success,
29462956
userdata.get_1(),
29472957
userdata.get_2(),
29482958
);
2959+
ctx.futures.write().complete(dup_id);
29492960
});
29502961

29512962
context
29522963
.global
29532964
.queue_on_submitted_work_done(queue_id, closure);
29542965

2955-
// TODO: Properly handle futures.
2956-
NULL_FUTURE
2966+
id.into()
29572967
}
29582968

29592969
#[no_mangle]
@@ -4742,3 +4752,46 @@ pub unsafe extern "C" fn wgpuRenderPassEncoderWriteTimestamp(
47424752
),
47434753
}
47444754
}
4755+
4756+
#[no_mangle]
4757+
pub unsafe extern "C" fn wgpuInstanceWaitAny(
4758+
instance: native::WGPUInstance,
4759+
future_count: usize,
4760+
futures: *mut native::WGPUFutureWaitInfo,
4761+
timeout_ns: u64,
4762+
) -> native::WGPUWaitStatus {
4763+
let instance = instance.as_ref().expect("invalid instance");
4764+
let context = &instance.context;
4765+
let futures = make_slice_mut(futures, future_count);
4766+
4767+
for future in futures.iter() {
4768+
assert_ne!(
4769+
future.future.id, NULL_FUTURE.id,
4770+
"null future should never be used"
4771+
);
4772+
}
4773+
4774+
let start = std::time::Instant::now();
4775+
loop {
4776+
let mut success = false;
4777+
let registry = context.futures.read();
4778+
for future in futures.iter_mut() {
4779+
future.completed = if registry.is_completed(future.future.into()) {
4780+
success = true;
4781+
true as native::WGPUBool
4782+
} else {
4783+
false as native::WGPUBool
4784+
}
4785+
}
4786+
drop(registry);
4787+
4788+
if success {
4789+
return native::WGPUWaitStatus_Success;
4790+
}
4791+
4792+
let now = std::time::Instant::now();
4793+
if now - start >= std::time::Duration::from_nanos(timeout_ns) {
4794+
return native::WGPUWaitStatus_TimedOut;
4795+
}
4796+
}
4797+
}

src/unimplemented.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,3 @@ pub extern "C" fn wgpuTextureViewSetLabel(
219219
) {
220220
unimplemented!();
221221
}
222-
223-
#[no_mangle]
224-
pub extern "C" fn wgpuInstanceWaitAny(
225-
_instance: native::WGPUInstance,
226-
_future_count: usize,
227-
_futures: *mut native::WGPUFutureWaitInfo,
228-
_timeout_ns: u64,
229-
) -> native::WGPUWaitStatus {
230-
unimplemented!();
231-
}

src/utils.rs

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use std::{borrow::Cow, ffi::CStr};
1+
use std::{borrow::Cow, ffi::CStr, sync::Arc};
2+
3+
use wgc::identity::IdentityManager;
24

35
use crate::native;
46

@@ -48,6 +50,17 @@ pub(crate) fn make_slice<'a, T: 'a>(ptr: *const T, len: usize) -> &'a [T] {
4850
}
4951
}
5052

53+
// Safer wrapper around `slice::from_raw_parts_mut` to handle
54+
// invalid `ptr` when `len` is zero.
55+
#[inline]
56+
pub(crate) fn make_slice_mut<'a, T: 'a>(ptr: *mut T, len: usize) -> &'a mut [T] {
57+
if len == 0 {
58+
&mut []
59+
} else {
60+
unsafe { std::slice::from_raw_parts_mut(ptr, len) }
61+
}
62+
}
63+
5164
#[inline]
5265
pub fn get_base_device_limits_from_adapter_limits(adapter_limits: &wgt::Limits) -> wgt::Limits {
5366
let default_limits = wgt::Limits::default();
@@ -566,3 +579,102 @@ pub fn test_get_base_device_limits_from_adapter_limits() {
566579
);
567580
}
568581
}
582+
583+
pub struct FutureIdMarker;
584+
impl wgc::id::Marker for FutureIdMarker {}
585+
586+
#[derive(Clone)]
587+
pub struct FutureId(wgc::id::Id<FutureIdMarker>);
588+
impl From<native::WGPUFuture> for FutureId {
589+
fn from(value: native::WGPUFuture) -> Self {
590+
FutureId(unsafe { std::mem::transmute(value.id) })
591+
}
592+
}
593+
impl Into<native::WGPUFuture> for FutureId {
594+
fn into(self) -> native::WGPUFuture {
595+
native::WGPUFuture {
596+
id: unsafe { std::mem::transmute(self) },
597+
}
598+
}
599+
}
600+
601+
// Somewhat borrowed from wgc's Registry and Storage types, which are crate-private
602+
pub struct FutureRegistry {
603+
identity: Arc<IdentityManager<FutureIdMarker>>,
604+
futures: Vec<FutureElement>,
605+
}
606+
607+
impl Default for FutureRegistry {
608+
fn default() -> Self {
609+
Self {
610+
identity: Arc::new(IdentityManager::new()),
611+
futures: Vec::new(),
612+
}
613+
}
614+
}
615+
616+
impl FutureRegistry {
617+
/// Test whether the future is completed.
618+
pub fn is_completed(&self, FutureId(id): FutureId) -> bool {
619+
let (idx, epoch) = id.unzip();
620+
let stored = self.futures.get(idx as usize);
621+
match stored {
622+
Some(FutureElement::Occupied {
623+
epoch: stored_epoch,
624+
}) => *stored_epoch == epoch,
625+
_ => true,
626+
}
627+
}
628+
629+
/// Creates a `FutureId` that's immediately completed. This is functionally
630+
/// identical to calling `incomplete_future` followed by `complete`
631+
pub fn completed_future(&mut self) -> FutureId {
632+
let id = self.identity.process();
633+
self.identity.free(id);
634+
return FutureId(id);
635+
}
636+
637+
/// Creates a `FutureId` that's incomplete. Call `complete` to mark the
638+
/// future completed.
639+
pub fn incomplete_future(&mut self) -> FutureId {
640+
let id = self.identity.process();
641+
let (idx, epoch) = id.unzip();
642+
if idx as usize >= self.futures.len() {
643+
self.futures
644+
.resize_with(idx as usize + 1, || FutureElement::Vacant);
645+
}
646+
// index is ensured with above resize
647+
let stored = self.futures.get_mut(idx as usize).unwrap();
648+
match std::mem::replace(stored, FutureElement::Occupied { epoch }) {
649+
FutureElement::Vacant => FutureId(id),
650+
FutureElement::Occupied {
651+
epoch: existing_epoch,
652+
..
653+
} => {
654+
// Storage does assert_ne! but i feel like this should always be an error
655+
unreachable!("Index {idx:?} of FutureId is already occupied (new epoch: {epoch}, existing epoch: {existing_epoch})")
656+
}
657+
}
658+
}
659+
660+
pub fn complete(&mut self, FutureId(id): FutureId) {
661+
let (idx, epoch) = id.unzip();
662+
let stored = self
663+
.futures
664+
.get_mut(idx as usize)
665+
.unwrap_or_else(|| panic!("FutureId[{id:?}] does not exist"));
666+
match std::mem::replace(stored, FutureElement::Vacant) {
667+
FutureElement::Vacant => panic!("Cannot remove a vacant resource"),
668+
FutureElement::Occupied {
669+
epoch: storage_epoch,
670+
} => {
671+
assert_eq!(epoch, storage_epoch, "id epoch mismatch");
672+
}
673+
}
674+
}
675+
}
676+
677+
enum FutureElement {
678+
Vacant,
679+
Occupied { epoch: u32 },
680+
}

0 commit comments

Comments
 (0)