Я обновляю проект до новейшей версии WGPU, которая добавляет параметр времени жизни в wgpu::Surface<'window>, что вызывает у меня проблемы с особенностью, которую Winit предоставляет для инициализации окна winit::application::ApplicationHandler.
Уменьшенная версия проблемы выглядит следующим образом:
use wgpu::{Dx12Compiler, Gles3MinorVersion, Instance, InstanceDescriptor, InstanceFlags, Surface};
use winit::{
application::ApplicationHandler,
event_loop::EventLoop,
window::{Window, WindowAttributes},
};
struct Application<'a> {
event_loop: EventLoop<()>,
window: Option<Window>,
surface: Option<Surface<'a>>,
}
impl<'a> ApplicationHandler for Application<'a> {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
if self.window.is_none() {
self.window = Some(
event_loop
.create_window(WindowAttributes::default())
.unwrap(),
);
let instance_descriptor = InstanceDescriptor {
backends: wgpu::Backends::VULKAN,
flags: InstanceFlags::default(),
dx12_shader_compiler: Dx12Compiler::Fxc,
gles_minor_version: Gles3MinorVersion::Automatic,
};
let instance = Instance::new(instance_descriptor);
self.surface = Some(
instance
.create_surface(self.window.as_ref().unwrap())
.unwrap(),
);
}
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId,
event: winit::event::WindowEvent,
) {
}
}
Приведенный выше код дает lifetime may not live long enough ссылку self.surface = Some(...), отмечая, что неявное время жизни &mut self не гарантируется до тех пор, пока 'a
Я не могу дополнить метод возобновленных признаков границами времени жизни, например resumed<'b: 'a>(&'b mut self, ...), поэтому я не вижу способа согласовать времена жизни.
Я также пробовал структурировать типы как таковые.
struct Inner<'a> {
window: Window,
surface: Option<Surface<'a>>
}
struct Outer<'a> {
event_loop: EventLoop<()>,
inner: Option<Inner<'a>>,
}
что позволяет мне объяснить, что время жизни окна гарантированно будет таким же, как и время жизни поверхности, но это, очевидно, не меняет ошибку компилятора и добавляет проблему циклических ссылок.
Итак, мой вопрос: учитывая подпись ApplicationHandler::resumed и требования к сроку службы, как нам создать поверхность wgpu?
Полное сообщение об ошибке
error: lifetime may not live long enough
--> src\main.rs:68:13
|
53 | impl<'a> ApplicationHandler for Application<'a> {
| -- lifetime `'a` defined here
54 | fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
| - let's call the lifetime of this reference `'1`
...
68 | self.surface = Some(
| ^^^^^^^^^^^^ assignment requires that `'1` must outlive `'a`

Вы правильно заметили, что у вас есть своего рода циклическая ссылка («самоссылающаяся структура»). Исправление состоит в том, чтобы сделать его некруглым:
┌────→ Surface ──┐
Application ├─→ Window
└────────────────┘
Вы делаете это, используя Arc, чтобы создать совместное владение окном.
struct Application {
window: Option<Arc<Window>>,
surface: Option<Surface<'static>>,
}
impl ApplicationHandler for Application {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
if self.window.is_none() {
let window = Arc::new(
event_loop
.create_window(WindowAttributes::default())
.unwrap(),
);
self.window = Some(window.clone()); // clone to share
let instance = Instance::new(...);
self.surface = Some(
instance
.create_surface(window) // do not use as_ref
.unwrap(),
);
}
}
Когда вы передаете Arc<Window> вместо &'a Window в create_surface(), возвращаемый Surface не имеет ограничений по времени жизни и может храниться как Surface<'static>.
Также обратите внимание: хотя это и не является частью заданного вами вопроса, вы не должны пытаться сохранить EventLoop в своей Application структуре. ‼Оно расходуется, когда начинаешь и ты уже не можешь его удержать.
Спасибо, Кевин, это сработало! Что касается цикла событий, я ценю дополнение; ранее я сохранял его в опции и принимал() перед его запуском, но создание его перед запуском имеет больше смысла