Есть небольшой рабочий набросок кода, который комплируются и запускается, создавая окно winit и instance + surface wgpu:
use std::sync::Arc;
use winit::event_loop::EventLoop;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow};
use winit::window::{Window, WindowId};
trait Renderer<'window> {
}
struct WGPURenderer<'window> {
instance: wgpu::Instance,
surface: wgpu::Surface<'window>
}
impl<'window> WGPURenderer<'window> {
async fn new(window: Arc<Window>) -> anyhow::Result<Self> {
let instance = wgpu::Instance::default();
let surface = instance.create_surface(Arc::clone(&window))?;
Ok(Self {
instance,
surface
})
}
}
impl<'window> Renderer<'window> for WGPURenderer<'window> {
}
#[derive(Default)]
struct App<'window> {
window: Option<Arc<Window>>,
renderer: Option<WGPURenderer<'window>>
}
impl ApplicationHandler for App<'_> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_none() {
let window_attrs = Window::default_attributes();
let window = Arc::new(
event_loop.create_window(window_attrs).
expect("Failed to create window")
);
self.window = Some(window.clone());
let renderer = pollster::block_on(WGPURenderer::new(window))
.expect("Failed to create renderer");
self.renderer = Some(renderer);
}
event_loop.set_control_flow(ControlFlow::Wait);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent
) {
match event {
WindowEvent::CloseRequested => {
event_loop.exit();
}
_ => {}
}
}
}
fn main() {
let event_loop = EventLoop::new()
.expect("Failed to create event loop");
let mut app = App::default();
event_loop.run_app(&mut app)
.expect("Failed to start event loop");
}
Проблема в том, что я хочу добавить косвенность - я не хочу прибивать гвоздями App к WGPURenderer, я хочу сделать возможными в будущем написать разные реализации трейта Renderer - например, VulkanRenderer, OpenGLRenderer и т. д.
Но стоит мне сделать:
#[derive(Default)]
struct App<'window> {
window: Option<Arc<Window>>,
renderer: Option<Arc<dyn Renderer<'window>>> // Тут может быть и Box
}
...
let renderer = pollster::block_on(WGPURenderer::new(window))
.expect("Failed to create renderer");
self.renderer = Some(Arc::new(renderer));
Я получаю:
error: lifetime may not live long enough
--> src/main.rs:29:25
|
17 | fn resumed(&mut self, event_loop: &ActiveEventLoop) {
| --------- has type `&mut App<'1>`
...
29 | self.renderer = Some(Arc::new(renderer));
| ^^^^^^^^^^^^^^^^^^ coercion requires that `'1` must outlive `'static`
Как правильно организовать архитектуру приложения, чтобы можно было иметь общий код App (где расположен код обработки событий ввода и т. п.) на все бекэнды рендера с учётом того, что WGPU требует лайфтайм окна?