Rust Vulkan图形编程库vulkano的使用,提供安全高效的Vulkan API封装与GPU加速计算

Rust Vulkan图形编程库vulkano的使用,提供安全高效的Vulkan API封装与GPU加速计算

Vulkano Logo

Vulkano是一个Rust包装器,围绕Vulkan图形API构建。它遵循Rust的设计哲学,只要你不使用unsafe代码,就不应该触发任何未定义行为。对于Vulkan来说,这意味着非unsafe代码应该始终符合有效的API使用规范。

Vulkano的功能

  • 提供围绕Vulkan的低级API,同时提供一些便利类型
  • 计划防止所有无效的API使用,即使是最晦涩的情况
  • 可以自动处理GPU端的同步(除非你选择自己处理)
  • 尝试提供便捷的使用体验

与其他图形API的比较

名称 开源时间 API级别 显著特性
Vulkano 2016年3月 高级Rust API包装Vulkan 类型安全的编译时着色器,与glsl和spir-v着色器代码的透明互操作性
Wgpu 2019年5月 多后端高级Rust API 支持Vulkan、Metal、DirectX、WebGPU等多个后端
Miniquad 2020年3月 多后端简约Rust API 适合中小型图形项目的简约API
Sierra 2021年3月 Vulkan/Metal高级Rust API 通过宏系统在Rust代码中构建布局、描述符和着色器类型
Glium 2014年10月 OpenGL高级Rust API 仅支持OpenGL
Ash 2016年8月 Vulkan低级API 不安全的Vulkan API绑定
Erupt 2020年4月 Vulkan低级API 不安全的Vulkan API绑定

示例代码

以下是一个简单的Vulkano计算示例:

use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::AutoCommandBufferBuilder;
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::{Device, DeviceExtensions};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::ComputePipeline;
use vulkano::sync::GpuFuture;
use vulkano::sync;

fn main() {
    // 初始化Vulkan实例
    let instance = Instance::new(None, &InstanceExtensions::none(), None)
        .expect("failed to create Vulkan instance");

    // 选择物理设备
    let physical = vulkano::instance::PhysicalDevice::enumerate(&instance)
        .next().expect("no device available");

    // 创建设备和队列
    let queue_family = physical.queue_families()
        .find(|&q| q.supports_compute())
        .expect("couldn't find a compute queue family");
    
    let (device, mut queues) = Device::new(physical, &physical.supported_features(),
        &DeviceExtensions::none(), [(queue_family, 0.5)].iter().cloned())
        .expect("failed to create device");
    
    let queue = queues.next().unwrap();

    // 创建计算管道
    mod cs {
        vulkano_shaders::shader!{
            ty: "compute",
            src: "
                #version 450
                
                layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
                
                layout(set = 0, binding = 0) buffer Data {
                    uint data[];
                };
                
                void main() {
                    uint idx = gl_GlobalInvocationID.x;
                    data[idx] *= 12;
                }
            "
        }
    }

    let shader = cs::Shader::load(device.clone())
        .expect("failed to create shader module");
    
    let compute_pipeline = ComputePipeline::new(device.clone(), &shader.main_entry_point(), &())
        .expect("failed to create compute pipeline");

    // 创建数据缓冲区
    let data_buffer = CpuAccessibleBuffer::from_iter(device.clone(), 
        BufferUsage::all(), 
        false, 
        (0..1024).map(|_| 1u32))
        .expect("failed to create buffer");

    // 创建描述符集
    let layout = compute_pipeline.layout().descriptor_set_layout(0).unwrap();
    let set = PersistentDescriptorSet::new(layout.clone(), [
        vulkano::descriptor_set::WriteDescriptorSet::buffer(0, data_buffer.clone()),
    ]).unwrap();

    // 构建命令缓冲区
    let mut builder = AutoCommandBufferBuilder::new(device.clone(), queue.family())
        .unwrap();
    builder.dispatch([1024 / 64, 1, 1], compute_pipeline.clone(), set.clone(), ())
        .unwrap();
    let command_buffer = builder.build().unwrap();

    // 执行计算
    let finished = command_buffer.execute(queue.clone()).unwrap();
    finished.then_signal_fence_and_flush().unwrap()
        .wait(None).unwrap();

    // 验证结果
    let content = data_buffer.read().unwrap();
    for (n, val) in content.iter().enumerate() {
        assert_eq!(*val, 12);
    }
    println!("Everything succeeded!");
}

完整示例

以下是一个更完整的三角形渲染示例:

use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage};
use vulkano::device::{Device, DeviceExtensions};
use vulkano::format::Format;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass};
use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo};
use vulkano::sync::{FlushError, GpuFuture};
use vulkano_win::VkSurfaceBuild;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;

#[derive(Default, Debug, Clone)]
struct Vertex {
    position: [f32; 2],
}

vulkano::impl_vertex!(Vertex, position);

fn main() {
    // 初始化窗口和事件循环
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    
    // 初始化Vulkan实例
    let instance = Instance::new(
        None,
        InstanceExtensions {
            khr_surface: true,
            ..InstanceExtensions::none()
        },
        None,
    )
    .unwrap();

    // 创建表面
    let surface = Surface::from_window(instance.clone(), window).unwrap();

    // 选择物理设备
    let device_extensions = DeviceExtensions {
        khr_swapchain: true,
        ..DeviceExtensions::none()
    };
    let (physical_device, queue_family) = vulkano::instance::PhysicalDevice::enumerate(&instance)
        .filter(|p| p.supported_extensions().contains(&device_extensions))
        .filter_map(|p| {
            p.queue_families()
                .find(|q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false))
                .map(|q| (p, q))
        })
        .min_by_key(|(p, _)| match p.properties().device_type {
            vulkano::device::PhysicalDeviceType::DiscreteGpu => 0,
            vulkano::device::PhysicalDeviceType::IntegratedGpu => 1,
            vulkano::device::PhysicalDeviceType::VirtualGpu => 2,
            vulkano::device::PhysicalDeviceType::Cpu => 3,
            _ => 4,
        })
        .unwrap();

    // 创建设备和队列
    let (device, mut queues) = Device::new(
        physical_device,
        &physical_device.supported_features(),
        &device_extensions,
        [(queue_family, 0.5)].iter().cloned(),
    )
    .unwrap();
    let queue = queues.next().unwrap();

    // 创建交换链
    let caps = surface.capabilities(physical_device).unwrap();
    let dimensions = window.inner_size();
    let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap();
    let format = caps.supported_formats[0].0;
    let swapchain = Swapchain::new(
        device.clone(),
        surface.clone(),
        SwapchainCreateInfo {
            min_image_count: caps.min_image_count,
            image_format: format,
            image_extent: dimensions.into(),
            image_usage: ImageUsage::color_attachment(),
            composite_alpha,
            ..Default::default()
        },
    )
    .unwrap();

    // 创建渲染通道
    let render_pass = vulkano::single_pass_renderpass!(
        device.clone(),
        attachments: {
            color: {
                format: swapchain.image_format(),
                samples: 1,
                load_op: Clear,
                store_op: Store,
            },
        },
        pass: {
            color: [color],
            depth_stencil: {},
        },
    )
    .unwrap();

    // 创建图形管道
    mod vs {
        vulkano_shaders::shader! {
            ty: "vertex",
            src: "
                #version 450
                
                layout(location = 0) in vec2 position;
                
                void main() {
                    gl_Position = vec4(position, 0.0, 1.0);
                }
            "
        }
    }

    mod fs {
        vulkano_shaders::shader! {
            ty: "fragment",
            src: "
                #version 450
                
                layout(location = 0) out vec4 f_color;
                
                void main() {
                    f_color = vec4(1.0, 0.0, 0.0, 1.0);
                }
            "
        }
    }

    let vs = vs::load(device.clone()).unwrap();
    let fs = fs::load(device.clone()).unwrap();

    let pipeline = GraphicsPipeline::start()
        .render_pass(render_pass.clone())
        .vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
        .input_assembly_state(InputAssemblyState::new())
        .vertex_shader(vs.entry_point("main").unwrap(), ())
        .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
        .fragment_shader(fs.entry_point("main").unwrap(), ())
        .build(device.clone())
        .unwrap();

    // 创建顶点缓冲区
    let vertex_buffer = CpuAccessibleBuffer::from_iter(
        device.clone(),
        BufferUsage::vertex_buffer(),
        false,
        [
            Vertex { position: [-0.5, -0.5] },
            Vertex { position: [0.0, 0.5] },
            Vertex { position: [0.5, -0.25] },
        ]
        .iter()
        .cloned(),
    )
    .unwrap();

    // 主循环
    let mut recreate_swapchain = false;
    let mut previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<dyn GpuFuture>);

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Poll;

        match event {
            winit::event::Event::WindowEvent { event: winit::event::WindowEvent::CloseRequested, .. } => {
                *control_flow = ControlFlow::Exit;
            },
            winit::event::Event::WindowEvent { event: winit::event::WindowEvent::Resized(_), .. } => {
                recreate_swapchain = true;
            },
            winit::event::Event::RedrawEventsCleared => {
                if recreate_swapchain {
                    let dimensions = window.inner_size();
                    let (new_swapchain, new_images) = swapchain.recreate(SwapchainCreateInfo {
                        image_extent: dimensions.into(),
                        ..swapchain.create_info()
                    }).unwrap();
                    swapchain = new_swapchain;
                    recreate_swapchain = false;
                }

                let (image_index, suboptimal, acquire_future) =
                    match vulkano::swapchain::acquire_next_image(swapchain.clone(), None) {
                        Ok(r) => r,
                        Err(e) => {
                            recreate_swapchain = true;
                            return;
                        }
                    };

                if suboptimal {
                    recreate_swapchain = true;
                }

                let viewport = Viewport {
                    origin: [0.0, 0.0],
                    dimensions: window.inner_size().into(),
                    depth_range: 0.0..1.0,
                };

                let framebuffer = Framebuffer::new(
                    render_pass.clone(),
                    FramebufferCreateInfo {
                        attachments: vec![swapchain.image_view(image_index).unwrap()],
                        ..Default::default()
                    },
                )
                .unwrap();

                let mut builder = AutoCommandBufferBuilder::primary(
                    device.clone(),
                    queue.family(),
                    CommandBufferUsage::OneTimeSubmit,
                )
                .unwrap();

                builder
                    .begin_render_pass(
                        framebuffer,
                        vulkano::command_buffer::SubpassContents::Inline,
                        vec![[0.0, 0.0, 1.0, 1.0].into()],
                    )
                    .unwrap()
                    .set_viewport(0, [viewport])
                    .bind_pipeline_graphics(pipeline.clone())
                    .bind_vertex_buffers(0, vertex_buffer.clone())
                    .draw(vertex_buffer.len() as u32, 1, 0, 0)
                    .unwrap()
                    .end_render_pass()
                    .unwrap();

                let command_buffer = builder.build().unwrap();

                let future = previous_frame_end
                    .take()
                    .unwrap()
                    .join(acquire_future)
                    .then_execute(queue.clone(), command_buffer)
                    .unwrap()
                    .then_swapchain_present(queue.clone(), swapchain.clone(), image_index)
                    .then_signal_fence_and_flush();

                match future {
                    Ok(future) => {
                        previous_frame_end = Some(Box::new(future) as Box<_>);
                    }
                    Err(FlushError::OutOfDate) => {
                        recreate_swapchain = true;
                        previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<_>);
                    }
                    Err(e) => {
                        println!("Failed to flush future: {:?}", e);
                        previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<_>);
                    }
                }
            },
            _ => (),
        }
    });
}

设置和故障排除

Vulkano使用shaderc-rs进行着色器编译。要提供预构建的libshaderc以加快构建时间,请参阅shaderc-rs文档。

注意,通常你不需要安装官方的Vulkan SDK。然而,macOS、iOS和tvOS平台需要更多的Vulkan设置,因为它不是原生支持的。

要构建libshaderc,必须安装以下工具并可在PATH上使用:

  • CMake
  • Ninja(使用MSVC构建时需要)
  • Python(在Windows上可执行文件必须命名为python.exe)

许可证

Vulkano采用以下许可证之一:

  • Apache License, Version 2.0
  • MIT license

你可以根据需要选择其中一种。


1 回复

Rust Vulkan图形编程库vulkano使用指南

概述

vulkano是一个Rust语言的Vulkan图形API封装库,提供了安全高效的Vulkan API访问方式,支持图形渲染和GPU加速计算。vulkano通过Rust的类型系统保证API调用的安全性,同时保持接近原生Vulkan的性能。

主要特性

  • 类型安全的Vulkan API封装
  • 自动资源管理
  • 错误处理内置
  • 支持计算管线
  • 同步原语抽象
  • 内存管理简化
  • 与Rust生态良好集成

安装方法

在Cargo.toml中添加依赖:

[dependencies]
vulkano = "0.34"
vulkano-shaders = "0.34"

基本使用示例

1. 初始化Vulkano

use vulkano::instance::{Instance, InstanceCreateInfo};

// 创建Vulkan实例
let instance = Instance::new(InstanceCreateInfo {
    enabled_extensions: vulkano_win::required_extensions(),  // 启用必要的扩展
    ..Default::default()  // 其他参数使用默认值
}).expect("failed to create instance");  // 错误处理

2. 创建窗口和表面

use winit::event_loop::{EventLoop};
use vulkano_win::VkSurfaceBuild;

// 创建事件循环和窗口
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();

// 从窗口创建Vulkan表面
let surface = vulkano_win::create_surface_from_winit(window, instance.clone()).unwrap();

3. 选择物理设备和队列族

use vulkano::device::{Device, DeviceCreateInfo, QueueCreateInfo};

// 枚举物理设备并选择第一个
let physical_device = vulkano::instance::PhysicalDevice::enumerate(&instance)
    .next().expect("no device available");

// 查找支持图形和表面呈现的队列族
let queue_family = physical_device.queue_families()
    .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap();

// 创建设备和队列
let (device, mut queues) = Device::new(
    physical_device,
    DeviceCreateInfo {
        queue_create_infos: vec![QueueCreateInfo::family(queue_family)],  // 指定队列族
        ..Default::default()
    },
).unwrap();

let queue = queues.next().unwrap();  // 获取队列

4. 创建交换链

use vulkano::swapchain::{Swapchain, SwapchainCreateInfo};

// 创建交换链
let (swapchain, images) = Swapchain::new(
    device.clone(),
    surface.clone(),
    SwapchainCreateInfo {
        min_image_count: 3,  // 最小图像数量
        image_format: Format::B8G8R8A8_SRGB,  // 图像格式
        image_extent: [1024, 768],  // 图像尺寸
        image_usage: ImageUsage::COLOR_ATTACHMENT,  // 图像用途
        composite_alpha: CompositeAlpha::Opaque,  // 透明度
        ..Default::default()
    },
).unwrap();

5. 创建渲染管线

use vulkano::pipeline::graphics::vertex_input::Vertex;
use vulkano_shaders::shader;

// 定义顶点着色器
mod vs {
    vulkano_shaders::shader!{
        ty: "vertex",
        src: "
            #version 450
            layout(location = 0) in vec2 position;
            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        "
    }
}

// 定义片段着色器
mod fs {
    vulkano_shaders::shader!{
        ty: "fragment",
        src: "
            #version 450
            layout(location = 0) out vec4 f_color;
            void main() {
                f_color = vec4(1.0, 0.0, 0.0, 1.0);
            }
        "
    }
}

// 加载着色器
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();

// 创建渲染通道
let render_pass = vulkano::single_pass_renderpass!(
    device.clone(),
    attachments: {
        color: {
            format: swapchain.image_format(),
            samples: 1,
            load_op: Clear,
            store_op: Store,
        },
    },
    pass: {
        color: [color],
        depth_stencil: {},
    },
).unwrap();

// 创建图形管线
let pipeline = GraphicsPipeline::start()
    .vertex_input_state(BuffersDefinition::new().vertex::<[f32; 2]>())  // 顶点输入
    .vertex_shader(vs.entry_point("main").unwrap(), ())  // 顶点着色器
    .input_assembly_state(InputAssemblyState::new())  // 输入装配
    .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())  // 视口状态
    .fragment_shader(fs.entry_point("main").unwrap(), ())  // 片段着色器
    .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())  // 渲染通道
    .build(device.clone())
    .unwrap();

6. 渲染循环

use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage};
use vulkano::sync::GpuFuture;

// 创建顶点缓冲区
let vertex_buffer = CpuAccessibleBuffer::from_iter(
    device.clone(),
    BufferUsage::VERTEX_BUFFER,
    false,
    vec![[-0.5, -0.5], [0.5, -0.5], [0.0, 0.5]].into_iter(),
).unwrap();

// 事件循环
event_loop.run(move |event, _, control_flow| {
    match event {
        Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
            *control_flow = ControlFlow::Exit;
        },
        Event::RedrawRequested(_) => {
            // 获取下一张交换链图像
            let (image_index, suboptimal, acquire_future) =
                swapchain::acquire_next_image(swapchain.clone(), None).unwrap();
            
            if suboptimal {
                // 处理交换链需要重建的情况
            }

            // 创建命令缓冲区
            let command_buffer = AutoCommandBufferBuilder::primary(
                device.clone(),
                queue.family(),
                CommandBufferUsage::OneTimeSubmit,
            )
            .unwrap()
            .begin_render_pass(
                RenderPassBeginInfo {
                    clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],  // 清除颜色
                    ..RenderPassBeginInfo::framebuffer(
                        framebuffers[image_index as usize].clone()
                    )
                },
                SubpassContents::Inline,
            )
            .unwrap()
            .bind_pipeline_graphics(pipeline.clone())  // 绑定管线
            .bind_vertex_buffers(0, vertex_buffer.clone())  // 绑定顶点缓冲区
            .draw(vertex_buffer.len() as u32, 1, 0, 0)  // 绘制命令
            .unwrap()
            .end_render_pass()
            .unwrap()
            .build()
            .unwrap();

            // 执行命令缓冲区并呈现
            let future = acquire_future
                .then_execute(queue.clone(), command_buffer)
                .unwrap()
                .then_swapchain_present(
                    queue.clone(),
                    SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index),
                )
                .then_signal_fence_and_flush()
                .unwrap();

            future.wait(None).unwrap();
        },
        _ => (),
    }
});

GPU计算示例

use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};

// 定义计算着色器
mod cs {
    vulkano_shaders::shader!{
        ty: "compute",
        src: "
            #version 450
            layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
            
            layout(set = 0, binding = 0) buffer Data {
                uint data[];
            } buf;
            
            void main() {
                uint idx = gl_GlobalInvocationID.x;
                buf.data[idx] = idx * 12;
            }
        "
    }
}

// 创建输入输出缓冲区
let data_buffer = CpuAccessibleBuffer::from_iter(
    device.clone(),
    BufferUsage::STORAGE_BUFFER,
    false,
    (0..65536u32).map(|_| 0u32),
).unwrap();

// 加载计算着色器
let shader = cs::load(device.clone()).unwrap();

// 创建计算管线
let compute_pipeline = ComputePipeline::new(
    device.clone(),
    shader.entry_point("main").unwrap(),
    &(),
    None,
    |_| {},
).unwrap();

// 创建描述符集
let layout = compute_pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
    layout.clone(),
    [WriteDescriptorSet::buffer(0, data_buffer.clone())],
).unwrap();

// 构建并执行命令缓冲区
let command_buffer = AutoCommandBufferBuilder::primary(
    device.clone(),
    queue.family(),
    CommandBufferUsage::OneTimeSubmit,
)
.unwrap()
.dispatch(
    [1024, 1, 1], // 工作组数量
    compute_pipeline.clone(),
    set.clone(),
    (),
)
.unwrap()
.build()
.unwrap();

// 执行计算
let future = sync::now(device.clone())
    .then_execute(queue.clone(), command_buffer)
    .unwrap()
    .then_signal_fence_and_flush()
    .unwrap();

future.wait(None).unwrap();

// 读取结果
let content = data_buffer.read().unwrap();
println!("Result: {:?}", &content[0..10]);

注意事项

  1. vulkano仍然在积极开发中,API可能会有变化
  2. 错误处理很重要,大多数操作返回Result类型
  3. 资源管理是自动的,但需要注意生命周期
  4. 同步原语的使用对性能有重要影响
  5. 对于复杂场景,可能需要手动管理内存和同步

完整示例

下面是一个完整的vulkano三角形渲染示例:

use vulkano::instance::{Instance, InstanceCreateInfo};
use vulkano_win::VkSurfaceBuild;
use winit::event_loop::{EventLoop, ControlFlow};
use winit::window::{WindowBuilder, Window};
use winit::event::{Event, WindowEvent};
use vulkano::device::{Device, DeviceCreateInfo, QueueCreateInfo};
use vulkano::swapchain::{Swapchain, SwapchainCreateInfo, Surface};
use vulkano::format::Format;
use vulkano::image::ImageUsage;
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
use vulkano::render_pass::Subpass;
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage};
use vulkano::sync::GpuFuture;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};

fn main() {
    // 1. 创建实例
    let instance = Instance::new(InstanceCreateInfo {
        enabled_extensions: vulkano_win::required_extensions(),
        ..Default::default()
    }).expect("failed to create instance");

    // 2. 创建窗口和表面
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    let surface = vulkano_win::create_surface_from_winit(window, instance.clone()).unwrap();

    // 3. 选择物理设备和队列族
    let physical_device = vulkano::instance::PhysicalDevice::enumerate(&instance)
        .next().expect("no device available");
    
    let queue_family = physical_device.queue_families()
        .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false))
        .expect("couldn't find a graphical queue family");

    let (device, mut queues) = Device::new(
        physical_device,
        DeviceCreateInfo {
            queue_create_infos: vec![QueueCreateInfo::family(queue_family)],
            ..Default::default()
        },
    ).unwrap();

    let queue = queues.next().unwrap();

    // 4. 创建交换链
    let (swapchain, images) = Swapchain::new(
        device.clone(),
        surface.clone(),
        SwapchainCreateInfo {
            min_image_count: 3,
            image_format: Format::B8G8R8A8_SRGB,
            image_extent: [1024, 768],
            image_usage: ImageUsage::COLOR_ATTACHMENT,
            composite_alpha: CompositeAlpha::Opaque,
            ..Default::default()
        },
    ).unwrap();

    // 5. 创建渲染管线
    mod vs {
        vulkano_shaders::shader!{
            ty: "vertex",
            src: "
                #version 450
                layout(location = 0) in vec2 position;
                void main() {
                    gl_Position = vec4(position, 0.0, 1.0);
                }
            "
        }
    }

    mod fs {
        vulkano_shaders::shader!{
            ty: "fragment",
            src: "
                #version 450
                layout(location = 0) out vec4 f_color;
                void main() {
                    f_color = vec4(1.0, 0.0, 0.0, 1.0);
                }
            "
        }
    }

    let vs = vs::load(device.clone()).unwrap();
    let fs = fs::load(device.clone()).unwrap();

    let render_pass = vulkano::single_pass_renderpass!(
        device.clone(),
        attachments: {
            color: {
                format: swapchain.image_format(),
                samples: 1,
                load_op: Clear,
                store_op: Store,
            },
        },
        pass: {
            color: [color],
            depth_stencil: {},
        },
    ).unwrap();

    let pipeline = GraphicsPipeline::start()
        .vertex_input_state(BuffersDefinition::new().vertex::<[f32; 2]>())
        .vertex_shader(vs.entry_point("main").unwrap(), ())
        .input_assembly_state(InputAssemblyState::new())
        .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
        .fragment_shader(fs.entry_point("main").unwrap(), ())
        .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
        .build(device.clone())
        .unwrap();

    // 创建帧缓冲
    let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone());

    // 创建顶点缓冲区
    let vertex_buffer = CpuAccessibleBuffer::from_iter(
        device.clone(),
        BufferUsage::VERTEX_BUFFER,
        false,
        vec![[-0.5, -0.5], [0.5, -0.5], [0.0, 0.5]].into_iter(),
    ).unwrap();

    // 6. 渲染循环
    event_loop.run(move |event, _, control_flow| {
        match event {
            Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
                *control_flow = ControlFlow::Exit;
            },
            Event::RedrawRequested(_) => {
                let (image_index, suboptimal, acquire_future) =
                    swapchain::acquire_next_image(swapchain.clone(), None).unwrap();
                
                if suboptimal {
                    // 重建交换链
                    let (new_swapchain, new_images) = swapchain.recreate(SwapchainCreateInfo {
                        image_extent: [window.inner_size().width, window.inner_size().height],
                        ..swapchain.create_info()
                    }).expect("failed to recreate swapchain");
                    
                    swapchain = new_swapchain;
                    framebuffers = window_size_dependent_setup(&new_images, render_pass.clone());
                }

                let command_buffer = AutoCommandBufferBuilder::primary(
                    device.clone(),
                    queue.family(),
                    CommandBufferUsage::OneTimeSubmit,
                )
                .unwrap()
                .begin_render_pass(
                    RenderPassBeginInfo {
                        clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
                        ..RenderPassBeginInfo::framebuffer(
                            framebuffers[image_index as usize].clone()
                        )
                    },
                    SubpassContents::Inline,
                )
                .unwrap()
                .bind_pipeline_graphics(pipeline.clone())
                .bind_vertex_buffers(0, vertex_buffer.clone())
                .draw(vertex_buffer.len() as u32, 1, 0, 0)
                .unwrap()
                .end_render_pass()
                .unwrap()
                .build()
                .unwrap();

                let future = acquire_future
                    .then_execute(queue.clone(), command_buffer)
                    .unwrap()
                    .then_swapchain_present(
                        queue.clone(),
                        SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index),
                    )
                    .then_signal_fence_and_flush()
                    .unwrap();

                future.wait(None).unwrap();
            },
            Event::WindowEvent { event: WindowEvent::Resized(_), .. } => {
                // 窗口大小改变时重建交换链
                let (new_swapchain, new_images) = swapchain.recreate(SwapchainCreateInfo {
                    image_extent: [window.inner_size().width, window.inner_size().height],
                    ..swapchain.create_info()
                }).expect("failed to recreate swapchain");
                
                swapchain = new_swapchain;
                framebuffers = window_size_dependent_setup(&new_images, render_pass.clone());
            },
            _ => (),
        }
回到顶部