Borrowed value does not live long enough in function with lifetimes

131 views Asked by At

I have a simple Rendrerer structure that is based on the wgpu crate:

const SHADER: &str = "
struct VertexInput {
    @location(0) position: vec3<f32>,
};

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
};

@vertex
fn vs_main(
    model: VertexInput,
) -> VertexOutput {
    var out: VertexOutput;
    out.clip_position = vec4<f32>(model.position, 1.0);
    return out;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    return vec4(1.0, 0.0, 0.0, 1.0);
}
";

struct Renderer {
    surface: wgpu::Surface,
    device: Rc<wgpu::Device>,
    queue: wgpu::Queue,
    config: wgpu::SurfaceConfiguration,
    render_pipeline: wgpu::RenderPipeline,
}

impl Renderer {
    async fn new(window: &Window) -> Self {
        // The instance is a handle to our GPU
        // Backends::all => Vulkan + Metal + DX12 + Browser WebGPU
        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
            backends: wgpu::Backends::all(),
            ..Default::default()
        });

        // # Safety
        //
        // The surface needs to live as long as the window that created it.
        // State owns the window so this should be safe.
        let surface = unsafe { instance.create_surface(&window) }.unwrap();

        let adapter = instance.request_adapter(
            &wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::default(),
                compatible_surface: Some(&surface),
                force_fallback_adapter: false,
            },
        ).await.unwrap();

        let (device, queue) = adapter.request_device(
            &wgpu::DeviceDescriptor {
                features: wgpu::Features::all_webgpu_mask(),
                // WebGL doesn't support all of wgpu's features, so if
                // we're building for the web we'll have to disable some.
                limits: wgpu::Limits::downlevel_defaults(),
                label: None,
            },
            None, // Trace path
        ).await.unwrap();

        let size = window.inner_size();
        let surface_caps = surface.get_capabilities(&adapter);
        // Shader code in this tutorial assumes an sRGB surface texture. Using a different
        // one will result all the colors coming out darker. If you want to support non
        // sRGB surfaces, you'll need to account for that when drawing to the frame.
        let surface_format = surface_caps.formats.iter()
            .copied()
            .find(|f| f.is_srgb())            
            .unwrap_or(surface_caps.formats[0]);
        let config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: surface_format,
            width: size.width,
            height: size.height,
            present_mode: surface_caps.present_modes[0],
            alpha_mode: surface_caps.alpha_modes[0],
            view_formats: vec![],
        };
        surface.configure(&device, &config);

        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
            label: Some("Shader"),
            source: wgpu::ShaderSource::Wgsl(SHADER.into()),
        });

        let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
            label: Some("Render Pipeline Layout"),
            bind_group_layouts: &[],
            push_constant_ranges: &[],
        });

        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
            label: Some("Render Pipeline"),
            layout: Some(&render_pipeline_layout),
            vertex: wgpu::VertexState {
                module: &shader,
                entry_point: "vs_main",
                buffers: &[
                    wgpu::VertexBufferLayout {
                        array_stride: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
                        step_mode: wgpu::VertexStepMode::Vertex,
                        attributes: &[
                            wgpu::VertexAttribute {
                                offset: 0,
                                shader_location: 0,
                                format: wgpu::VertexFormat::Float32x3,
                            },
                        ],
                    }
                ],
            },
            fragment: Some(wgpu::FragmentState {
                module: &shader,
                entry_point: "fs_main",
                targets: &[Some(wgpu::ColorTargetState {
                    format: config.format,
                    blend: Some(wgpu::BlendState::REPLACE),
                    write_mask: wgpu::ColorWrites::ALL,
                })],
            }),
            primitive: wgpu::PrimitiveState {
                topology: wgpu::PrimitiveTopology::TriangleList,
                strip_index_format: None,
                front_face: wgpu::FrontFace::Ccw,
                cull_mode: Some(wgpu::Face::Back),
                // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
                polygon_mode: wgpu::PolygonMode::Fill,
                // Requires Features::DEPTH_CLIP_CONTROL
                unclipped_depth: false,
                // Requires Features::CONSERVATIVE_RASTERIZATION
                conservative: false,
            },
            depth_stencil: None,
            multisample: wgpu::MultisampleState {
                count: 1,
                mask: !0,
                alpha_to_coverage_enabled: false,
            },
            multiview: None,
        });

        Self {
            surface,
            device: Rc::new(device),
            queue,
            config,
            render_pipeline,
        }
    }

    pub fn render(&mut self, mut handler: impl FnMut(wgpu::RenderPass<'_>) -> ()) {
        let output = self.surface.get_current_texture().unwrap();
        
        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
        let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
            label: Some("Render Encoder"),
        });
        {
            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: Some("Render Pass"),
                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                    view: &view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color {
                            r: 0.3 as f64,
                            g: 0.3 as f64,
                            b: 0.3 as f64,
                            a: 1.0 as f64,
                        }),
                        store: wgpu::StoreOp::Store,
                    },
                })],
                depth_stencil_attachment: None,
                timestamp_writes: None,
                occlusion_query_set: None,
            });

            render_pass.set_pipeline(&self.render_pipeline);
            handler(render_pass);
        }

        // submit will accept anything that implements IntoIter
        self.queue.submit(std::iter::once(encoder.finish()));
        output.present();
    }
}

Few structures and traits for the model entity:

struct Mesh {
    vertices: Vec<f32>,
    vertex_buffer: wgpu::Buffer,
}

impl Mesh {
    pub(crate) fn new(device: Rc<wgpu::Device>, vertices: Vec<f32>) -> Self {
        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
            label: Some("Vertex Buffer"),
            contents: bytemuck::cast_slice(&vertices),
            usage: wgpu::BufferUsages::VERTEX,
        });
        Self {
            vertices: vertices,
            vertex_buffer,
        }
    }
}

pub trait DrawMesh<'a> {
    fn draw_mesh(&mut self, mesh: &'a Mesh);
}

impl<'a, 'b> DrawMesh<'b> for wgpu::RenderPass<'a> where 'b: 'a {

    fn draw_mesh(&mut self, mesh: &'b Mesh) {
        self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
        self.draw(0..mesh.vertices.len() as u32, 0..1);
    }
}

trait LifeCycle {
    fn on_update<'a, 'b>(&mut self, render_pass: &mut wgpu::RenderPass<'a>) where 'b: 'a;
}

type MeshRef = Rc<RefCell<Mesh>>;

struct Model3D {
    mesh: MeshRef,
}

impl Model3D {
    pub fn new(mesh: MeshRef) -> Self {
        Self {
            mesh,
        }
    }

    pub fn mesh(&self) -> MeshRef {
        self.mesh.clone()
    }
}

impl LifeCycle for Model3D {
    fn on_update<'a, 'b>(&mut self, render_pass: &mut wgpu::RenderPass<'a>) where 'b: 'a {
        let mesh: Ref<'b, Mesh> = self.mesh.borrow();
        render_pass.draw_mesh(&mesh);
    }
}

And entrypoint:

async fn run() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    let renderer = Rc::new(RefCell::new(Renderer::new(&window).await));

    let mesh = Rc::new(
        RefCell::new(
            Mesh::new(renderer.borrow().device.clone(), vec![0.5, 0.5, 0.0])
        )
    );
    let mut model = Model3D::new(mesh);

    event_loop.run(move |event, _, _| match event {
        winit::event::Event::RedrawRequested(_window_id) => {
            renderer.borrow_mut().render(|mut render_pass| {
                model.on_update(&mut render_pass);
            });
        },
        _ => {}
    });
}

fn main() {
    pollster::block_on(run());
}

Unfortunately, this code is compiled with error:

error: lifetime may not live long enough
  --> src/main.rs:70:9
   |
68 |     fn on_update<'a, 'b>(&mut self, render_pass: &mut wgpu::RenderPass<'a>) where 'b: 'a {
   |                  --      - let's call the lifetime of this reference `'1`
   |                  |
   |                  lifetime `'a` defined here
69 |         let mesh: Ref<'b, Mesh> = self.mesh.borrow();
70 |         render_pass.draw_mesh(&mesh);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`

error[E0597]: `mesh` does not live long enough
  --> src/main.rs:70:31
   |
68 |     fn on_update<'a, 'b>(&mut self, render_pass: &mut wgpu::RenderPass<'a>) where 'b: 'a {
   |                  -- lifetime `'a` defined here
69 |         let mesh: Ref<'b, Mesh> = self.mesh.borrow();
   |             ---- binding `mesh` declared here
70 |         render_pass.draw_mesh(&mesh);
   |         ----------------------^^^^^-
   |         |                     |
   |         |                     borrowed value does not live long enough
   |         argument requires that `mesh` is borrowed for `'a`
71 |     }
   |     - `mesh` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.
error: could not compile `game` (bin "game") due to 2 previous errors

And I can't get how to bypass it without unsafe blocks.

Cargo.toml:

[package]
name = "game"
version = "0.1.0"
edition = "2021"

[dependencies]
bytemuck = { version = "1.14.0", features = ["derive"] }
pollster = "0.3"
wgpu = "0.18.0"
winit = "0.28.7"

P.S. The code looks stupidly in some moments due to I simplified my original project as much as possible. All Rc, RefCell and LifeCycle implementation are required in the original project, so I can't store a just Mesh structure instead of Rc<RefCell<Mesh>>.

0

There are 0 answers