Rust Vulkan图形编程库vulkano的使用,提供安全高效的Vulkan API封装与GPU加速计算
Rust Vulkan图形编程库vulkano的使用,提供安全高效的Vulkan API封装与GPU加速计算
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]);
注意事项
- vulkano仍然在积极开发中,API可能会有变化
- 错误处理很重要,大多数操作返回
Result
类型 - 资源管理是自动的,但需要注意生命周期
- 同步原语的使用对性能有重要影响
- 对于复杂场景,可能需要手动管理内存和同步
完整示例
下面是一个完整的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());
},
_ => (),
}