-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Get Bevy building for WebAssembly with multithreading #12205
Changes from 9 commits
9bfeb2e
9e5fc79
eb846f7
93a6042
59eddb5
106f892
431aa37
1c3d4da
9783726
e183932
7e34b01
a14f55e
65cca19
0636540
dd7240c
e8fab22
b99c936
29fcef3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,23 +104,54 @@ pub fn render_system(world: &mut World, state: &mut SystemState<Query<Entity, Wi | |
} | ||
} | ||
|
||
/// A wrapper to safely make `wgpu` types Send / Sync on web with atomics enabled. | ||
/// On web with atomics enabled the inner value can only be accessed on the | ||
/// `wgpu` thread or else a panic will occur. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should probably be noted that it will panic if dropped in another thread as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @daxpedda Good call out. I updated the comment. |
||
/// On other platforms the wrapper simply contains the wrapped value. | ||
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] | ||
#[derive(Debug, Clone, Deref, DerefMut)] | ||
pub struct WgpuWrapper<T>(T); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be ideal if this doesn't need to be public, as it's sort of an implementation detail. Perhaps we should make a wrapper for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @james7132 I'd also prefer it to be non-public, but the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK sounds good to me. I believe @robtfm recently found that we may not need these wrappers anymore, but we can tackle that in a separate PR. |
||
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] | ||
#[derive(Debug, Clone, Deref, DerefMut)] | ||
pub struct WgpuWrapper<T>(send_wrapper::SendWrapper<T>); | ||
|
||
// SAFETY: SendWrapper is always Send + Sync. | ||
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] | ||
unsafe impl<T> Send for WgpuWrapper<T> {} | ||
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] | ||
unsafe impl<T> Sync for WgpuWrapper<T> {} | ||
|
||
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] | ||
impl<T> WgpuWrapper<T> { | ||
pub fn new(t: T) -> Self { | ||
Self(t) | ||
} | ||
} | ||
|
||
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] | ||
impl<T> WgpuWrapper<T> { | ||
pub fn new(t: T) -> Self { | ||
Self(send_wrapper::SendWrapper::new(t)) | ||
} | ||
} | ||
|
||
/// This queue is used to enqueue tasks for the GPU to execute asynchronously. | ||
#[derive(Resource, Clone, Deref, DerefMut)] | ||
pub struct RenderQueue(pub Arc<Queue>); | ||
pub struct RenderQueue(pub Arc<WgpuWrapper<Queue>>); | ||
|
||
/// The handle to the physical device being used for rendering. | ||
/// See [`Adapter`] for more info. | ||
#[derive(Resource, Clone, Debug, Deref, DerefMut)] | ||
pub struct RenderAdapter(pub Arc<Adapter>); | ||
pub struct RenderAdapter(pub Arc<WgpuWrapper<Adapter>>); | ||
|
||
/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], | ||
/// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces). | ||
#[derive(Resource, Clone, Deref, DerefMut)] | ||
pub struct RenderInstance(pub Arc<Instance>); | ||
pub struct RenderInstance(pub Arc<WgpuWrapper<Instance>>); | ||
|
||
/// The [`AdapterInfo`] of the adapter in use by the renderer. | ||
#[derive(Resource, Clone, Deref, DerefMut)] | ||
pub struct RenderAdapterInfo(pub AdapterInfo); | ||
pub struct RenderAdapterInfo(pub WgpuWrapper<AdapterInfo>); | ||
|
||
const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") { | ||
"Unable to find a GPU! Make sure you have installed required drivers! For extra information, see: https://github.com/bevyengine/bevy/blob/latest/docs/linux_dependencies.md" | ||
|
@@ -287,12 +318,12 @@ pub async fn initialize_renderer( | |
) | ||
.await | ||
.unwrap(); | ||
let queue = Arc::new(queue); | ||
let adapter = Arc::new(adapter); | ||
let queue = Arc::new(WgpuWrapper::new(queue)); | ||
let adapter = Arc::new(WgpuWrapper::new(adapter)); | ||
( | ||
RenderDevice::from(device), | ||
RenderQueue(queue), | ||
RenderAdapterInfo(adapter_info), | ||
RenderAdapterInfo(WgpuWrapper::new(adapter_info)), | ||
RenderAdapter(adapter), | ||
) | ||
} | ||
|
@@ -377,7 +408,10 @@ impl<'w> RenderContext<'w> { | |
/// buffer. | ||
pub fn add_command_buffer_generation_task( | ||
&mut self, | ||
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] | ||
task: impl FnOnce(RenderDevice) -> CommandBuffer + 'w + Send, | ||
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] | ||
task: impl FnOnce(RenderDevice) -> CommandBuffer + 'w, | ||
) { | ||
self.flush_encoder(); | ||
|
||
|
@@ -393,27 +427,46 @@ impl<'w> RenderContext<'w> { | |
self.flush_encoder(); | ||
|
||
let mut command_buffers = Vec::with_capacity(self.command_buffer_queue.len()); | ||
let mut task_based_command_buffers = ComputeTaskPool::get().scope(|task_pool| { | ||
for (i, queued_command_buffer) in self.command_buffer_queue.into_iter().enumerate() { | ||
match queued_command_buffer { | ||
QueuedCommandBuffer::Ready(command_buffer) => { | ||
command_buffers.push((i, command_buffer)); | ||
} | ||
QueuedCommandBuffer::Task(command_buffer_generation_task) => { | ||
let render_device = self.render_device.clone(); | ||
if self.force_serial { | ||
command_buffers | ||
.push((i, command_buffer_generation_task(render_device))); | ||
} else { | ||
task_pool.spawn(async move { | ||
(i, command_buffer_generation_task(render_device)) | ||
}); | ||
|
||
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] | ||
{ | ||
let mut task_based_command_buffers = ComputeTaskPool::get().scope(|task_pool| { | ||
for (i, queued_command_buffer) in self.command_buffer_queue.into_iter().enumerate() | ||
{ | ||
match queued_command_buffer { | ||
QueuedCommandBuffer::Ready(command_buffer) => { | ||
command_buffers.push((i, command_buffer)); | ||
} | ||
QueuedCommandBuffer::Task(command_buffer_generation_task) => { | ||
let render_device = self.render_device.clone(); | ||
if self.force_serial { | ||
command_buffers | ||
.push((i, command_buffer_generation_task(render_device))); | ||
} else { | ||
task_pool.spawn(async move { | ||
(i, command_buffer_generation_task(render_device)) | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
command_buffers.append(&mut task_based_command_buffers); | ||
} | ||
|
||
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] | ||
for (i, queued_command_buffer) in self.command_buffer_queue.into_iter().enumerate() { | ||
match queued_command_buffer { | ||
QueuedCommandBuffer::Ready(command_buffer) => { | ||
command_buffers.push((i, command_buffer)); | ||
} | ||
QueuedCommandBuffer::Task(command_buffer_generation_task) => { | ||
let render_device = self.render_device.clone(); | ||
command_buffers.push((i, command_buffer_generation_task(render_device))); | ||
} | ||
} | ||
}); | ||
command_buffers.append(&mut task_based_command_buffers); | ||
} | ||
|
||
command_buffers.sort_unstable_by_key(|(i, _)| *i); | ||
command_buffers.into_iter().map(|(_, cb)| cb).collect() | ||
} | ||
|
@@ -428,5 +481,8 @@ impl<'w> RenderContext<'w> { | |
|
||
enum QueuedCommandBuffer<'w> { | ||
Ready(CommandBuffer), | ||
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] | ||
Task(Box<dyn FnOnce(RenderDevice) -> CommandBuffer + 'w + Send>), | ||
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] | ||
Task(Box<dyn FnOnce(RenderDevice) -> CommandBuffer + 'w>), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI is failing because it also needs this target
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That wasn't the issue, CI is still complaining: