pub struct DeferredContext { /* private fields */ }winit only.Expand description
Defer the contstruction of the RenderContext until a surface is
constructed.
WebGL really screws up the normal workflow of “first create your wgpu
handles, then make windows and stuff”, because creating the
wgpu::Adapter
requires a
wgpu::Surface
to be passed in after creating it with a
wgpu::Instance
using the configuration of a
winit::window::Window.
To top it all off, this painful back-and-forth involves async.
On web, this struct caches a builder and constructs the RenderContext
in a JS thread oppurtunistically when it is used to construct a
SurfaceWindow.
Native platforms block the thread when constructing, it’s not an operation
that should take very long. pollster doesn’t seem to get along with JS
however.
See example usage below. There’s a bit of jumping through hoops, but most of it is handled in the background.
impl ApplicationHandler for YourApp {
fn resume(&mut self, event_loop: &ActiveEventLoop) {
// Create a window and provide the surface to the deferred context to start building.
// In the worst case (web), at this point it will have an unconfigured surface and no config.
let window = self.window.get_or_init(|| {
let mut attributes = WindowAttributes::default();
#[cfg(target_arch = "wasm32")]
attributes.with_canvas(get_webgl_canvas());
// On web, will provide the surface to the context builder so it can
// start building the context. The surface isn't configured until it's needed.
self.deferred_context.create_window(event_loop, attributes, Default::default())
.expect("Couldn't create window");
});
// Can't do much else until we have a valid context, which may take until
// the next function call to finish building. Move on to main loop.
self.suspended = false;
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: _, event: _) {
if self.suspended { return; }
let context = match self.defered_context.get_context() {
Ok(c) => c,
// Something went wrong building the context, abort.
Err(GetDeferredContextError::Build(e)) => panic!("Error building context: {e}"),
// resume() didn't run for some reason, RenderContext has no surface
// to use to initialize with (on web).
Err(GetDeferredContextError::RequiresSurface) => unreachable!("Surface should have been provided in resume()"),
// Still building, will try again next window_event().
Err(GetDeferredContextError::StillBuilding) => return,
};
// Now that the context is ready, we can finish configuring the window surface.
// init_surface() will create everything when needed and make sure it's configured.
let active_window = self.window.get().expect("Window should have created in resume()")
.init_with_context(&context).expect("Couldn't init window");
// Side-note: Vertex/shader buffers, pipelines, etc. are also dependent on the
// RenderContext being configured (with a surface on web).
// Put the buffers in an Option/OnceCell and generate them ASAP.
let buffers_and_shit = self.buffers_and_shit.get_or_insert_with(|| init_buffers_and_shit(&context, active_window));
Self::finally_render(context, active_window);
}
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
// Android requires surfaces be dropped on suspend. init_surface() above
// will re-create and configure the window surface automatically when unsuspended.
self.window.get_mut().map(|w| w.drop_surface());
self.suspended = true;
}
}Implementations§
Source§impl DeferredContext
impl DeferredContext
Sourcepub fn new(builder: RenderContextBuilder) -> Self
Available on crate features webgl or poll only.
pub fn new(builder: RenderContextBuilder) -> Self
webgl or poll only.Create a new deferred builder from a regular builder, with the appropriate method for the platform.
On web, this will call Self::new_requiring_surface; on native with the
poll feature, Self::new_poll.
Sourcepub fn new_requiring_surface(builder: RenderContextBuilder) -> Self
Available on crate features webgl or poll only.
pub fn new_requiring_surface(builder: RenderContextBuilder) -> Self
webgl or poll only.Create a new deferred builder from a regular builder, in “waiting-for-surface” mode. On native you don’t have to use this unless you really want to validate the surface format is compatible.
Sourcepub async fn new_async(builder: RenderContextBuilder) -> Self
Available on non-crate feature webgl only.
pub async fn new_async(builder: RenderContextBuilder) -> Self
webgl only.Create a future that immediately tries to await the finalization of a
RenderContextBuilder.
It’s not really intended to use mew in an async environment - the app as a whole can contain async logic, sure, but rendering stuff deserves its own system thread.
Sourcepub fn new_poll(builder: RenderContextBuilder) -> Self
Available on non-crate feature webgl and crate feature poll only.
pub fn new_poll(builder: RenderContextBuilder) -> Self
webgl and crate feature poll only.Immediately try to finalize a RenderContextBuilder, by blocking on
Self::new_async.
Realistically, it should not take long at all to build a RenderContext.
Even if it does, it should be fine to block the main render thread because,
well, what are you going to render without a context?
Sourcepub fn create_window(
&self,
event_loop: &ActiveEventLoop,
attributes: WindowAttributes,
config_opts: SurfaceConfigOptions,
) -> Result<SurfaceWindow, CreateWindowError>
pub fn create_window( &self, event_loop: &ActiveEventLoop, attributes: WindowAttributes, config_opts: SurfaceConfigOptions, ) -> Result<SurfaceWindow, CreateWindowError>
Create a SurfaceWindow, and at the same time, provide the builder with
a WebGL surface so it can create a valid
wgpu::Adapter.
In effect, you can almost ignore the whole initialization workflow - just make a window in the event loop, and then get the context to draw to it. If you can’t get the context, try again until you can.
Sourcepub fn get_instance(&self) -> Instance
pub fn get_instance(&self) -> Instance
Get a clone of the
wgpu::Instance,
which may have been created automatically.
Sourcepub fn get_context(&self) -> Result<&RenderContext, GetDeferredContextError>
pub fn get_context(&self) -> Result<&RenderContext, GetDeferredContextError>
Try to get the RenderContext.
If it hasn’t been built yet, or if it ran into an error while building,
you’ll get an Err(_) - try again next frame.
On web, make sure to create a window with Self::create_window, as you
can’t get a
wgpu::Adapter
before providing a
wgpu::Surface.
Makes sense, right? Totally not backwards? That’s why I made this.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for DeferredContext
impl !RefUnwindSafe for DeferredContext
impl Send for DeferredContext
impl !Sync for DeferredContext
impl Unpin for DeferredContext
impl UnsafeUnpin for DeferredContext
impl !UnwindSafe for DeferredContext
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.