Fix wayland resize issue (#1089)

Some drivers dont enforce a swapchain extent, so you need to check for
resize events from the os and then recreate the swapchain.
I took this opportunity to refactor the examples a bunch.
I want to do some more refactoring to make things consistent but I'll
leave that for a follow up PR.
This commit is contained in:
Lucas Kent 2018-10-28 08:16:30 +11:00 committed by GitHub
parent 753ee299d8
commit 2153177210
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 430 additions and 321 deletions

View File

@ -64,6 +64,7 @@ fn main() {
let mut events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
let window = surface.window();
let queue_family = physical.queue_families().find(|&q| { let queue_family = physical.queue_families().find(|&q| {
q.supports_graphics() && surface.is_supported(q).unwrap_or(false) q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
@ -86,7 +87,12 @@ fn main() {
let (mut swapchain, mut images) = { let (mut swapchain, mut images) = {
let caps = surface.capabilities(physical) let caps = surface.capabilities(physical)
.expect("failed to get surface capabilities"); .expect("failed to get surface capabilities");
dimensions = caps.current_extent.unwrap_or([1024, 768]); let dimensions = if let Some(dimensions) = window.get_inner_size() {
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
[dimensions.0, dimensions.1]
} else {
return;
};
let alpha = caps.supported_composite_alpha.iter().next().unwrap(); let alpha = caps.supported_composite_alpha.iter().next().unwrap();
let format = caps.supported_formats[0].0; let format = caps.supported_formats[0].0;
Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format, Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format,
@ -108,9 +114,12 @@ fn main() {
previous_frame_end.cleanup_finished(); previous_frame_end.cleanup_finished();
if recreate_swapchain { if recreate_swapchain {
dimensions = surface.capabilities(physical) dimensions = if let Some(dimensions) = window.get_inner_size() {
.expect("failed to get surface capabilities") let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
.current_extent.unwrap(); [dimensions.0, dimensions.1]
} else {
return;
};
let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) { let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) {
Ok(r) => r, Ok(r) => r,
@ -125,8 +134,7 @@ fn main() {
recreate_swapchain = false; recreate_swapchain = false;
} }
let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), None) {
None) {
Ok(r) => r, Ok(r) => r,
Err(AcquireError::OutOfDate) => { Err(AcquireError::OutOfDate) => {
recreate_swapchain = true; recreate_swapchain = true;
@ -179,6 +187,7 @@ fn main() {
events_loop.poll_events(|ev| { events_loop.poll_events(|ev| {
match ev { match ev {
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true, winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true,
winit::Event::WindowEvent { event: winit::WindowEvent::Resized(_), .. } => recreate_swapchain = true,
_ => () _ => ()
} }
}); });

View File

@ -19,6 +19,12 @@ extern crate vulkano_win;
use vulkano_win::VkSurfaceBuild; use vulkano_win::VkSurfaceBuild;
use vulkano::sync::GpuFuture; use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader; use vulkano_shaders::vulkano_shader;
use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, RenderPassAbstract};
use vulkano::command_buffer::DynamicState;
use vulkano::image::SwapchainImage;
use vulkano::pipeline::viewport::Viewport;
use winit::Window;
use std::sync::Arc; use std::sync::Arc;
@ -35,8 +41,7 @@ fn main() {
let mut events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
let window = surface.window();
let mut dimensions;
let queue_family = physical.queue_families().find(|&q| q.supports_graphics() && let queue_family = physical.queue_families().find(|&q| q.supports_graphics() &&
surface.is_supported(q).unwrap_or(false)) surface.is_supported(q).unwrap_or(false))
@ -51,10 +56,17 @@ fn main() {
.expect("failed to create device"); .expect("failed to create device");
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
let (mut swapchain, mut images) = { let (mut swapchain, images) = {
let caps = surface.capabilities(physical).expect("failed to get surface capabilities"); let caps = surface.capabilities(physical).expect("failed to get surface capabilities");
dimensions = caps.current_extent.unwrap_or([1024, 768]); let dimensions = if let Some(dimensions) = window.get_inner_size() {
// convert to physical pixels
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
[dimensions.0, dimensions.1]
} else {
// The window no longer exists so exit the application.
return;
};
let usage = caps.supported_usage_flags; let usage = caps.supported_usage_flags;
let alpha = caps.supported_composite_alpha.iter().next().unwrap(); let alpha = caps.supported_composite_alpha.iter().next().unwrap();
let format = caps.supported_formats[0].0; let format = caps.supported_formats[0].0;
@ -83,7 +95,7 @@ fn main() {
let vs = vs::Shader::load(device.clone()).expect("failed to create shader module"); let vs = vs::Shader::load(device.clone()).expect("failed to create shader module");
let fs = fs::Shader::load(device.clone()).expect("failed to create shader module"); let fs = fs::Shader::load(device.clone()).expect("failed to create shader module");
let renderpass = Arc::new( let render_pass = Arc::new(
single_pass_renderpass!(device.clone(), single_pass_renderpass!(device.clone(),
attachments: { attachments: {
color: { color: {
@ -127,7 +139,7 @@ fn main() {
.viewports_dynamic_scissors_irrelevant(1) .viewports_dynamic_scissors_irrelevant(1)
.fragment_shader(fs.main_entry_point(), ()) .fragment_shader(fs.main_entry_point(), ())
.blend_alpha_blending() .blend_alpha_blending()
.render_pass(vulkano::framebuffer::Subpass::from(renderpass.clone(), 0).unwrap()) .render_pass(vulkano::framebuffer::Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone()) .build(device.clone())
.unwrap()); .unwrap());
@ -136,74 +148,48 @@ fn main() {
.build().unwrap() .build().unwrap()
); );
let mut framebuffers: Option<Vec<Arc<vulkano::framebuffer::Framebuffer<_,_>>>> = None; let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None };
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut recreate_swapchain = false; let mut recreate_swapchain = false;
let mut previous_frame_end = Box::new(tex_future) as Box<GpuFuture>; let mut previous_frame_end = Box::new(tex_future) as Box<GpuFuture>;
let mut dynamic_state = vulkano::command_buffer::DynamicState {
line_width: None,
viewports: Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
};
loop { loop {
previous_frame_end.cleanup_finished(); previous_frame_end.cleanup_finished();
if recreate_swapchain { if recreate_swapchain {
let dimensions = if let Some(dimensions) = window.get_inner_size() {
dimensions = surface.capabilities(physical) let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
.expect("failed to get surface capabilities") [dimensions.0, dimensions.1]
.current_extent.unwrap_or([1024, 768]); } else {
return;
};
let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) { let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) {
Ok(r) => r, Ok(r) => r,
Err(vulkano::swapchain::SwapchainCreationError::UnsupportedDimensions) => { Err(vulkano::swapchain::SwapchainCreationError::UnsupportedDimensions) => continue,
continue;
},
Err(err) => panic!("{:?}", err) Err(err) => panic!("{:?}", err)
}; };
swapchain = new_swapchain; swapchain = new_swapchain;
images = new_images; framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state);
framebuffers = None;
dynamic_state.viewports = Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]);
recreate_swapchain = false; recreate_swapchain = false;
} }
if framebuffers.is_none() { let (image_num, future) = match vulkano::swapchain::acquire_next_image(swapchain.clone(), None) {
framebuffers = Some(images.iter().map(|image| {
Arc::new(vulkano::framebuffer::Framebuffer::start(renderpass.clone())
.add(image.clone()).unwrap()
.build().unwrap())
}).collect::<Vec<_>>());
}
let (image_num, future) = match vulkano::swapchain::acquire_next_image(swapchain.clone(),
None) {
Ok(r) => r, Ok(r) => r,
Err(vulkano::swapchain::AcquireError::OutOfDate) => { Err(vulkano::swapchain::AcquireError::OutOfDate) => {
recreate_swapchain = true; recreate_swapchain = true;
continue; continue;
}, }
Err(err) => panic!("{:?}", err) Err(err) => panic!("{:?}", err)
}; };
let cb = vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()) let cb = vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family())
.unwrap() .unwrap()
.begin_render_pass( .begin_render_pass(
framebuffers.as_ref().unwrap()[image_num].clone(), false, framebuffers[image_num].clone(), false,
vec![[0.0, 0.0, 1.0, 1.0].into()]).unwrap() vec![[0.0, 0.0, 1.0, 1.0].into()]).unwrap()
.draw(pipeline.clone(), .draw(pipeline.clone(),
&dynamic_state, &dynamic_state,
@ -235,6 +221,7 @@ fn main() {
events_loop.poll_events(|ev| { events_loop.poll_events(|ev| {
match ev { match ev {
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true, winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true,
winit::Event::WindowEvent { event: winit::WindowEvent::Resized(_), .. } => recreate_swapchain = true,
_ => () _ => ()
} }
}); });
@ -242,6 +229,30 @@ fn main() {
} }
} }
/// This method is called once during initialization then again whenever the window is resized
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPassAbstract + Send + Sync>,
dynamic_state: &mut DynamicState
) -> Vec<Arc<FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
};
dynamic_state.viewports = Some(vec!(viewport));
images.iter().map(|image| {
Arc::new(
Framebuffer::start(render_pass.clone())
.add(image.clone()).unwrap()
.build().unwrap()
) as Arc<FramebufferAbstract + Send + Sync>
}).collect::<Vec<_>>()
}
vulkano_shader!{ vulkano_shader!{
mod_name: vs, mod_name: vs,
ty: "vertex", ty: "vertex",

View File

@ -35,20 +35,21 @@ use vulkano::descriptor::pipeline_layout::PipelineLayoutDescPcRange;
use vulkano::device::Device; use vulkano::device::Device;
use vulkano::device::DeviceExtensions; use vulkano::device::DeviceExtensions;
use vulkano::format; use vulkano::format;
use vulkano::framebuffer::Framebuffer; use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract};
use vulkano::framebuffer::Subpass; use vulkano::image::SwapchainImage;
use vulkano::pipeline::GraphicsPipeline; use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::shader::GraphicsShaderType; use vulkano::pipeline::shader::{GraphicsShaderType, ShaderInterfaceDef, ShaderInterfaceDefEntry, ShaderModule};
use vulkano::pipeline::shader::ShaderInterfaceDef;
use vulkano::pipeline::shader::ShaderInterfaceDefEntry;
use vulkano::pipeline::shader::ShaderModule;
use vulkano::pipeline::vertex::SingleBufferDefinition; use vulkano::pipeline::vertex::SingleBufferDefinition;
use vulkano::pipeline::viewport::Viewport; use vulkano::pipeline::viewport::Viewport;
use vulkano::swapchain::Swapchain; use vulkano::swapchain::{AcquireError, PresentMode, SurfaceTransform, Swapchain, SwapchainCreationError};
use vulkano::swapchain;
use vulkano::sync::GpuFuture; use vulkano::sync::GpuFuture;
use vulkano::sync;
use vulkano_win::VkSurfaceBuild; use vulkano_win::VkSurfaceBuild;
use winit::Window;
use std::borrow::Cow; use std::borrow::Cow;
use std::ffi::CStr; use std::ffi::CStr;
use std::fs::File; use std::fs::File;
@ -73,12 +74,9 @@ fn main() {
.next() .next()
.expect("no graphics device"); .expect("no graphics device");
let mut events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let surface = winit::WindowBuilder::new() let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
.with_decorations(false) let window = surface.window();
.with_title("particle storm") let (device, mut queues) = {
.build_vk_surface(&events_loop, instance.clone())
.unwrap();
let (graphics_device, mut queues) = {
let graphical_queue_family = physical let graphical_queue_family = physical
.queue_families() .queue_families()
.find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false))
@ -96,17 +94,17 @@ fn main() {
}; };
let graphics_queue = queues.next().unwrap(); let graphics_queue = queues.next().unwrap();
let (swapchain, images) = { let (mut swapchain, images) = {
let caps = surface let caps = surface
.capabilities(graphics_device.physical_device()) .capabilities(device.physical_device())
.expect("failure to get surface capabilities"); .expect("failure to get surface capabilities");
let alpha = caps.supported_composite_alpha.iter().next().unwrap();
let format = caps.supported_formats[0].0; let format = caps.supported_formats[0].0;
let dimensions = caps.current_extent.unwrap_or([1024, 768]); let dimensions = caps.current_extent.unwrap_or([1024, 768]);
let usage = caps.supported_usage_flags; let usage = caps.supported_usage_flags;
let present = caps.present_modes.iter().next().unwrap();
Swapchain::new( Swapchain::new(
graphics_device.clone(), device.clone(),
surface.clone(), surface.clone(),
caps.min_image_count, caps.min_image_count,
format, format,
@ -114,17 +112,17 @@ fn main() {
1, 1,
usage, usage,
&graphics_queue, &graphics_queue,
vk::swapchain::SurfaceTransform::Identity, SurfaceTransform::Identity,
vk::swapchain::CompositeAlpha::Opaque, alpha,
present, PresentMode::Fifo,
true, true,
None, None,
).expect("failed to create swapchain") ).expect("failed to create swapchain")
}; };
let renderpass = Arc::new( let render_pass = Arc::new(
single_pass_renderpass!( single_pass_renderpass!(
graphics_device.clone(), attachments: { device.clone(), attachments: {
color: { color: {
load: Clear, load: Clear,
store: Store, store: Store,
@ -146,7 +144,7 @@ fn main() {
f.read_to_end(&mut v).unwrap(); f.read_to_end(&mut v).unwrap();
// Create a ShaderModule on a device the same Shader::load does it. // Create a ShaderModule on a device the same Shader::load does it.
// NOTE: You will have to verify correctness of the data by yourself! // NOTE: You will have to verify correctness of the data by yourself!
unsafe { ShaderModule::new(graphics_device.clone(), &v) }.unwrap() unsafe { ShaderModule::new(device.clone(), &v) }.unwrap()
}; };
let fs = { let fs = {
@ -154,7 +152,7 @@ fn main() {
.expect("Can't find file src/bin/runtime-shader/frag.spv"); .expect("Can't find file src/bin/runtime-shader/frag.spv");
let mut v = vec![]; let mut v = vec![];
f.read_to_end(&mut v).unwrap(); f.read_to_end(&mut v).unwrap();
unsafe { ShaderModule::new(graphics_device.clone(), &v) }.unwrap() unsafe { ShaderModule::new(device.clone(), &v) }.unwrap()
}; };
// This structure will tell Vulkan how input entries of our vertex shader // This structure will tell Vulkan how input entries of our vertex shader
@ -387,25 +385,20 @@ fn main() {
.vertex_input(SingleBufferDefinition::<Vertex>::new()) .vertex_input(SingleBufferDefinition::<Vertex>::new())
.vertex_shader(vert_main, ()) .vertex_shader(vert_main, ())
.triangle_list() .triangle_list()
.viewports([ .viewports_dynamic_scissors_irrelevant(1)
Viewport {
origin: [0.0, 0.0],
depth_range: 0.0..1.0,
dimensions: [images[0].dimensions()[0] as f32,
images[0].dimensions()[1] as f32],
},
].iter().cloned())
.fragment_shader(frag_main, ()) .fragment_shader(frag_main, ())
.cull_mode_front() .cull_mode_front()
.front_face_counter_clockwise() .front_face_counter_clockwise()
.depth_stencil_disabled() .depth_stencil_disabled()
.render_pass(Subpass::from(renderpass.clone(), 0).unwrap()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(graphics_device.clone()) .build(device.clone())
.unwrap(), .unwrap(),
); );
let mut recreate_swapchain = false;
let vertex_buffer = CpuAccessibleBuffer::from_iter( let vertex_buffer = CpuAccessibleBuffer::from_iter(
graphics_device.clone(), device.clone(),
BufferUsage::all(), BufferUsage::all(),
[ [
Vertex { position: [-1.0, 1.0], color: [1.0, 0.0, 0.0] }, Vertex { position: [-1.0, 1.0], color: [1.0, 0.0, 0.0] },
@ -418,27 +411,45 @@ fn main() {
// note that passing wrong types, providing sets at wrong indexes will cause // note that passing wrong types, providing sets at wrong indexes will cause
// descriptor set builder to return Err! // descriptor set builder to return Err!
let framebuffers: Vec<_> = images let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None };
.iter() let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
.map(|image| Arc::new( let mut previous_frame_end = Box::new(sync::now(device.clone())) as Box<GpuFuture>;
Framebuffer::start(renderpass.clone())
.add(image.clone()).unwrap()
.build().unwrap(),
))
.collect();
loop { loop {
events_loop.poll_events(|_| ()); previous_frame_end.cleanup_finished();
let (image_num, acquire_future) = if recreate_swapchain {
vk::swapchain::acquire_next_image( // Get the new dimensions for the viewport/framebuffers.
swapchain.clone(), let dimensions = if let Some(dimensions) = window.get_inner_size() {
None, let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
).expect("failed to acquire swapchain in time"); [dimensions.0, dimensions.1]
} else {
return;
};
let command_buffer = AutoCommandBufferBuilder let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) {
::new( Ok(r) => r,
graphics_device.clone(), Err(SwapchainCreationError::UnsupportedDimensions) => continue,
Err(err) => panic!("{:?}", err)
};
swapchain = new_swapchain;
framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state);
recreate_swapchain = false;
}
let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), None) {
Ok(r) => r,
Err(AcquireError::OutOfDate) => {
recreate_swapchain = true;
continue;
},
Err(err) => panic!("{:?}", err)
};
let command_buffer = AutoCommandBufferBuilder::new(
device.clone(),
graphics_queue.family(), graphics_queue.family(),
).unwrap() ).unwrap()
.begin_render_pass( .begin_render_pass(
@ -448,7 +459,7 @@ fn main() {
).unwrap() ).unwrap()
.draw( .draw(
graphics_pipeline.clone(), graphics_pipeline.clone(),
&DynamicState::none(), &dynamic_state,
vertex_buffer.clone(), vertex_buffer.clone(),
(), (),
(), (),
@ -456,10 +467,57 @@ fn main() {
.end_render_pass().unwrap() .end_render_pass().unwrap()
.build().unwrap(); .build().unwrap();
acquire_future let future = previous_frame_end.join(acquire_future)
.then_execute(graphics_queue.clone(), command_buffer).unwrap() .then_execute(graphics_queue.clone(), command_buffer).unwrap()
.then_swapchain_present(graphics_queue.clone(), swapchain.clone(), image_num) .then_swapchain_present(graphics_queue.clone(), swapchain.clone(), image_num)
.then_signal_fence_and_flush().unwrap() .then_signal_fence_and_flush();
.wait(None).unwrap();
match future {
Ok(future) => {
previous_frame_end = Box::new(future) as Box<_>;
}
Err(vulkano::sync::FlushError::OutOfDate) => {
recreate_swapchain = true;
previous_frame_end = Box::new(vulkano::sync::now(device.clone())) as Box<_>;
}
Err(e) => {
println!("{:?}", e);
previous_frame_end = Box::new(vulkano::sync::now(device.clone())) as Box<_>;
}
}
let mut done = false;
events_loop.poll_events(|ev| {
match ev {
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true,
winit::Event::WindowEvent { event: winit::WindowEvent::Resized(_), .. } => recreate_swapchain = true,
_ => ()
}
});
if done { return; }
} }
} }
/// This method is called once during initialization then again whenever the window is resized
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPassAbstract + Send + Sync>,
dynamic_state: &mut DynamicState
) -> Vec<Arc<FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
};
dynamic_state.viewports = Some(vec!(viewport));
images.iter().map(|image| {
Arc::new(
Framebuffer::start(render_pass.clone())
.add(image.clone()).unwrap()
.build().unwrap()
) as Arc<FramebufferAbstract + Send + Sync>
}).collect::<Vec<_>>()
}

View File

@ -18,84 +18,95 @@ extern crate vulkano_shaders;
extern crate vulkano_win; extern crate vulkano_win;
use vulkano_win::VkSurfaceBuild; use vulkano_win::VkSurfaceBuild;
use vulkano::buffer::cpu_pool::CpuBufferPool;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
use vulkano::device::{Device, DeviceExtensions};
use vulkano::format::Format;
use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract};
use vulkano::image::SwapchainImage;
use vulkano::image::attachment::AttachmentImage;
use vulkano::instance::Instance;
use vulkano::instance::PhysicalDevice;
use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineAbstract};
use vulkano::pipeline::vertex::TwoBuffersDefinition;
use vulkano::pipeline::viewport::Viewport;
use vulkano::swapchain::{AcquireError, PresentMode, SurfaceTransform, Swapchain, SwapchainCreationError};
use vulkano::swapchain;
use vulkano::sync::GpuFuture; use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader; use vulkano_shaders::vulkano_shader;
use winit::Window;
use cgmath::{Matrix3, Matrix4, Point3, Vector3, Rad};
use examples::{Vertex, Normal, VERTICES, NORMALS, INDICES};
use std::sync::Arc; use std::sync::Arc;
use std::iter;
fn main() { fn main() {
// The start of this example is exactly the same as `triangle`. You should read the // The start of this example is exactly the same as `triangle`. You should read the
// `triangle` example if you haven't done so yet. // `triangle` example if you haven't done so yet.
let extensions = vulkano_win::required_extensions(); let extensions = vulkano_win::required_extensions();
let instance = vulkano::instance::Instance::new(None, &extensions, None).expect("failed to create instance"); let instance = Instance::new(None, &extensions, None).expect("failed to create instance");
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance) let physical = PhysicalDevice::enumerate(&instance).next().expect("no device available");
.next().expect("no device available");
println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); println!("Using device: {} (type: {:?})", physical.name(), physical.ty());
let mut events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
let window = surface.window();
let mut dimensions; // unlike the triangle example we need to keep track of the width and height so we can calculate
// render the teapot with the correct aspect ratio.
let queue_family = physical.queue_families().find(|&q| q.supports_graphics() && let mut dimensions = if let Some(dimensions) = window.get_inner_size() {
surface.is_supported(q).unwrap_or(false)) let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
.expect("couldn't find a graphical queue family"); [dimensions.0, dimensions.1]
} else {
let device_ext = vulkano::device::DeviceExtensions { return;
khr_swapchain: true,
.. vulkano::device::DeviceExtensions::none()
}; };
let (device, mut queues) = vulkano::device::Device::new(physical, physical.supported_features(), let queue_family = physical.queue_families().find(|&q|
&device_ext, [(queue_family, 0.5)].iter().cloned()) q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
.expect("failed to create device"); ).expect("couldn't find a graphical queue family");
let device_ext = DeviceExtensions { khr_swapchain: true, .. DeviceExtensions::none() };
let (device, mut queues) = Device::new(
physical, physical.supported_features(), &device_ext, [(queue_family, 0.5)].iter().cloned()
).expect("failed to create device");
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
let (mut swapchain, mut images) = { let (mut swapchain, images) = {
let caps = surface.capabilities(physical).expect("failed to get surface capabilities"); let caps = surface.capabilities(physical).expect("failed to get surface capabilities");
dimensions = caps.current_extent.unwrap_or([1024, 768]);
let usage = caps.supported_usage_flags; let usage = caps.supported_usage_flags;
let format = caps.supported_formats[0].0; let format = caps.supported_formats[0].0;
let alpha = caps.supported_composite_alpha.iter().next().unwrap(); let alpha = caps.supported_composite_alpha.iter().next().unwrap();
vulkano::swapchain::Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format, dimensions, 1, Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format, dimensions, 1,
usage, &queue, vulkano::swapchain::SurfaceTransform::Identity, usage, &queue, SurfaceTransform::Identity, alpha, PresentMode::Fifo, true, None).expect("failed to create swapchain")
alpha,
vulkano::swapchain::PresentMode::Fifo, true, None).expect("failed to create swapchain")
}; };
let vertices = VERTICES.iter().cloned();
let vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), vertices).unwrap();
let mut depth_buffer = vulkano::image::attachment::AttachmentImage::transient(device.clone(), dimensions, vulkano::format::D16Unorm).unwrap(); let normals = NORMALS.iter().cloned();
let normals_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), normals).unwrap();
let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer let indices = INDICES.iter().cloned();
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), examples::VERTICES.iter().cloned()) let index_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), indices).unwrap();
.expect("failed to create buffer");
let normals_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer let uniform_buffer = CpuBufferPool::<vs::ty::Data>::new(device.clone(), BufferUsage::all());
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), examples::NORMALS.iter().cloned())
.expect("failed to create buffer");
let index_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer
::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), examples::INDICES.iter().cloned())
.expect("failed to create buffer");
// note: this teapot was meant for OpenGL where the origin is at the lower left
// instead the origin is at the upper left in Vulkan, so we reverse the Y axis
let mut proj = cgmath::perspective(cgmath::Rad(std::f32::consts::FRAC_PI_2), { dimensions[0] as f32 / dimensions[1] as f32 }, 0.01, 100.0);
let view = cgmath::Matrix4::look_at(cgmath::Point3::new(0.3, 0.3, 1.0), cgmath::Point3::new(0.0, 0.0, 0.0), cgmath::Vector3::new(0.0, -1.0, 0.0));
let scale = cgmath::Matrix4::from_scale(0.01);
let uniform_buffer = vulkano::buffer::cpu_pool::CpuBufferPool::<vs::ty::Data>
::new(device.clone(), vulkano::buffer::BufferUsage::all());
let vs = vs::Shader::load(device.clone()).expect("failed to create shader module"); let vs = vs::Shader::load(device.clone()).expect("failed to create shader module");
let fs = fs::Shader::load(device.clone()).expect("failed to create shader module"); let fs = fs::Shader::load(device.clone()).expect("failed to create shader module");
let renderpass = Arc::new( let render_pass = Arc::new(
single_pass_renderpass!(device.clone(), single_pass_renderpass!(device.clone(),
attachments: { attachments: {
color: { color: {
@ -118,86 +129,53 @@ fn main() {
).unwrap() ).unwrap()
); );
let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::start() let (mut pipeline, mut framebuffers) = window_size_dependent_setup(device.clone(), &vs, &fs, &images, render_pass.clone());
.vertex_input(vulkano::pipeline::vertex::TwoBuffersDefinition::new())
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.fragment_shader(fs.main_entry_point(), ())
.depth_stencil_simple_depth()
.render_pass(vulkano::framebuffer::Subpass::from(renderpass.clone(), 0).unwrap())
.build(device.clone())
.unwrap());
let mut framebuffers: Option<Vec<Arc<vulkano::framebuffer::Framebuffer<_,_>>>> = None;
let mut recreate_swapchain = false; let mut recreate_swapchain = false;
let mut previous_frame = Box::new(vulkano::sync::now(device.clone())) as Box<GpuFuture>; let mut previous_frame = Box::new(vulkano::sync::now(device.clone())) as Box<GpuFuture>;
let rotation_start = std::time::Instant::now(); let rotation_start = std::time::Instant::now();
let mut dynamic_state = vulkano::command_buffer::DynamicState {
line_width: None,
viewports: Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
};
loop { loop {
previous_frame.cleanup_finished(); previous_frame.cleanup_finished();
if recreate_swapchain { if recreate_swapchain {
dimensions = if let Some(dimensions) = window.get_inner_size() {
dimensions = surface.capabilities(physical) let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
.expect("failed to get surface capabilities") [dimensions.0, dimensions.1]
.current_extent.unwrap_or([1024, 768]); } else {
return;
};
let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) { let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) {
Ok(r) => r, Ok(r) => r,
Err(vulkano::swapchain::SwapchainCreationError::UnsupportedDimensions) => { Err(SwapchainCreationError::UnsupportedDimensions) => continue,
continue;
},
Err(err) => panic!("{:?}", err) Err(err) => panic!("{:?}", err)
}; };
swapchain = new_swapchain; swapchain = new_swapchain;
images = new_images;
depth_buffer = vulkano::image::attachment::AttachmentImage::transient(device.clone(), dimensions, vulkano::format::D16Unorm).unwrap(); let (new_pipeline, new_framebuffers) = window_size_dependent_setup(device.clone(), &vs, &fs, &new_images, render_pass.clone());
pipeline = new_pipeline;
framebuffers = None; framebuffers = new_framebuffers;
proj = cgmath::perspective(cgmath::Rad(std::f32::consts::FRAC_PI_2), { dimensions[0] as f32 / dimensions[1] as f32 }, 0.01, 100.0);
dynamic_state.viewports = Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]);
recreate_swapchain = false; recreate_swapchain = false;
} }
if framebuffers.is_none() {
framebuffers = Some(images.iter().map(|image| {
Arc::new(vulkano::framebuffer::Framebuffer::start(renderpass.clone())
.add(image.clone()).unwrap()
.add(depth_buffer.clone()).unwrap()
.build().unwrap())
}).collect::<Vec<_>>());
}
let uniform_buffer_subbuffer = { let uniform_buffer_subbuffer = {
let elapsed = rotation_start.elapsed(); let elapsed = rotation_start.elapsed();
let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0;
let rotation = cgmath::Matrix3::from_angle_y(cgmath::Rad(rotation as f32)); let rotation = Matrix3::from_angle_y(Rad(rotation as f32));
// note: this teapot was meant for OpenGL where the origin is at the lower left
// instead the origin is at the upper left in Vulkan, so we reverse the Y axis
let aspect_ratio = dimensions[0] as f32 / dimensions[1] as f32;
let proj = cgmath::perspective(Rad(std::f32::consts::FRAC_PI_2), aspect_ratio, 0.01, 100.0);
let view = Matrix4::look_at(Point3::new(0.3, 0.3, 1.0), Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, -1.0, 0.0));
let scale = Matrix4::from_scale(0.01);
let uniform_data = vs::ty::Data { let uniform_data = vs::ty::Data {
world : cgmath::Matrix4::from(rotation).into(), world: Matrix4::from(rotation).into(),
view : (view * scale).into(), view: (view * scale).into(),
proj : proj.into(), proj: proj.into(),
}; };
uniform_buffer.next(uniform_data).unwrap() uniform_buffer.next(uniform_data).unwrap()
@ -208,27 +186,26 @@ fn main() {
.build().unwrap() .build().unwrap()
); );
let (image_num, acquire_future) = match vulkano::swapchain::acquire_next_image(swapchain.clone(), let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), None) {
None) {
Ok(r) => r, Ok(r) => r,
Err(vulkano::swapchain::AcquireError::OutOfDate) => { Err(AcquireError::OutOfDate) => {
recreate_swapchain = true; recreate_swapchain = true;
continue; continue;
}, }
Err(err) => panic!("{:?}", err) Err(err) => panic!("{:?}", err)
}; };
let command_buffer = vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap() let command_buffer = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap()
.begin_render_pass( .begin_render_pass(
framebuffers.as_ref().unwrap()[image_num].clone(), false, framebuffers[image_num].clone(), false,
vec![ vec![
[0.0, 0.0, 1.0, 1.0].into(), [0.0, 0.0, 1.0, 1.0].into(),
1f32.into() 1f32.into()
]).unwrap() ]).unwrap()
.draw_indexed( .draw_indexed(
pipeline.clone(), pipeline.clone(),
&dynamic_state, &DynamicState::none(),
(vertex_buffer.clone(), normals_buffer.clone()), vec!(vertex_buffer.clone(), normals_buffer.clone()),
index_buffer.clone(), set.clone(), ()).unwrap() index_buffer.clone(), set.clone(), ()).unwrap()
.end_render_pass().unwrap() .end_render_pass().unwrap()
.build().unwrap(); .build().unwrap();
@ -256,6 +233,7 @@ fn main() {
events_loop.poll_events(|ev| { events_loop.poll_events(|ev| {
match ev { match ev {
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true, winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true,
winit::Event::WindowEvent { event: winit::WindowEvent::Resized(_), .. } => recreate_swapchain = true,
_ => () _ => ()
} }
}); });
@ -263,6 +241,50 @@ fn main() {
} }
} }
/// This method is called once during initialization then again whenever the window is resized
fn window_size_dependent_setup(
device: Arc<Device>,
vs: &vs::Shader,
fs: &fs::Shader,
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPassAbstract + Send + Sync>,
) -> (Arc<GraphicsPipelineAbstract + Send + Sync>, Vec<Arc<FramebufferAbstract + Send + Sync>> ) {
let dimensions = images[0].dimensions();
let depth_buffer = AttachmentImage::transient(device.clone(), dimensions, Format::D16Unorm).unwrap();
let framebuffers = images.iter().map(|image| {
Arc::new(
Framebuffer::start(render_pass.clone())
.add(image.clone()).unwrap()
.add(depth_buffer.clone()).unwrap()
.build().unwrap()
) as Arc<FramebufferAbstract + Send + Sync>
}).collect::<Vec<_>>();
// In the triangle example we use a dynamic viewport, as its a simple example.
// However in the teapot example, we recreate the pipelines with a hardcoded viewport instead.
// This allows the driver to optimize things, at the cost of slower window resizes.
// https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport
let pipeline = Arc::new(GraphicsPipeline::start()
.vertex_input(TwoBuffersDefinition::<Vertex, Normal>::new())
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.viewports(iter::once(Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}))
.fragment_shader(fs.main_entry_point(), ())
.depth_stencil_simple_depth()
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())
.unwrap());
(pipeline, framebuffers)
}
vulkano_shader!{ vulkano_shader!{
mod_name: vs, mod_name: vs,
ty: "vertex", ty: "vertex",

View File

@ -26,26 +26,22 @@ extern crate vulkano_win;
use vulkano_win::VkSurfaceBuild; use vulkano_win::VkSurfaceBuild;
use vulkano::buffer::BufferUsage; use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::buffer::CpuAccessibleBuffer; use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
use vulkano::command_buffer::AutoCommandBufferBuilder;
use vulkano::command_buffer::DynamicState;
use vulkano::device::Device; use vulkano::device::Device;
use vulkano::framebuffer::Framebuffer; use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract};
use vulkano::framebuffer::Subpass; use vulkano::image::SwapchainImage;
use vulkano::instance::Instance; use vulkano::instance::Instance;
use vulkano::pipeline::GraphicsPipeline; use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::viewport::Viewport; use vulkano::pipeline::viewport::Viewport;
use vulkano::swapchain; use vulkano::swapchain;
use vulkano::swapchain::PresentMode; use vulkano::swapchain::{AcquireError, PresentMode, SurfaceTransform, Swapchain, SwapchainCreationError};
use vulkano::swapchain::SurfaceTransform;
use vulkano::swapchain::Swapchain;
use vulkano::swapchain::AcquireError;
use vulkano::swapchain::SwapchainCreationError;
use vulkano::sync::now; use vulkano::sync::now;
use vulkano::sync::GpuFuture; use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader; use vulkano_shaders::vulkano_shader;
use winit::Window;
use std::sync::Arc; use std::sync::Arc;
vulkano_shader!{ vulkano_shader!{
@ -148,6 +144,7 @@ fn main() {
let mut events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
let window = surface.window();
let queue = physical.queue_families().find(|&q| { let queue = physical.queue_families().find(|&q| {
q.supports_graphics() && surface.is_supported(q).unwrap_or(false) q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
@ -163,13 +160,17 @@ fn main() {
}; };
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
let mut dimensions; let mut dimensions = if let Some(dimensions) = window.get_inner_size() {
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
[dimensions.0, dimensions.1]
} else {
return;
};
let (mut swapchain, mut images) = { let (mut swapchain, images) = {
let caps = surface.capabilities(physical) let caps = surface.capabilities(physical)
.expect("failed to get surface capabilities"); .expect("failed to get surface capabilities");
dimensions = caps.current_extent.unwrap_or([1024, 768]);
let alpha = caps.supported_composite_alpha.iter().next().unwrap(); let alpha = caps.supported_composite_alpha.iter().next().unwrap();
let format = caps.supported_formats[0].0; let format = caps.supported_formats[0].0;
@ -234,25 +235,20 @@ fn main() {
.build(device.clone()) .build(device.clone())
.unwrap()); .unwrap());
let mut framebuffers: Option<Vec<Arc<vulkano::framebuffer::Framebuffer<_,_>>>> = None;
let mut recreate_swapchain = false; let mut recreate_swapchain = false;
let mut previous_frame_end = Box::new(now(device.clone())) as Box<GpuFuture>; let mut previous_frame_end = Box::new(now(device.clone())) as Box<GpuFuture>;
let mut dynamic_state = DynamicState { let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None };
line_width: None, let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
};
loop { loop {
previous_frame_end.cleanup_finished(); previous_frame_end.cleanup_finished();
if recreate_swapchain { if recreate_swapchain {
dimensions = surface.capabilities(physical) dimensions = if let Some(dimensions) = window.get_inner_size() {
.expect("failed to get surface capabilities") let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
.current_extent.unwrap(); [dimensions.0, dimensions.1]
} else {
return;
};
let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) { let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) {
Ok(r) => r, Ok(r) => r,
@ -263,27 +259,12 @@ fn main() {
}; };
swapchain = new_swapchain; swapchain = new_swapchain;
images = new_images; framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state);
framebuffers = None;
dynamic_state.viewports = Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]);
recreate_swapchain = false; recreate_swapchain = false;
} }
if framebuffers.is_none() { let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), None) {
framebuffers = Some(images.iter().map(|image| {
Arc::new(Framebuffer::start(render_pass.clone())
.add(image.clone()).unwrap()
.build().unwrap())
}).collect::<Vec<_>>());
}
let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(),
None) {
Ok(r) => r, Ok(r) => r,
Err(AcquireError::OutOfDate) => { Err(AcquireError::OutOfDate) => {
recreate_swapchain = true; recreate_swapchain = true;
@ -293,8 +274,7 @@ fn main() {
}; };
let command_buffer = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap() let command_buffer = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap()
.begin_render_pass(framebuffers.as_ref().unwrap()[image_num].clone(), false, .begin_render_pass(framebuffers[image_num].clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()])
vec![[0.0, 0.0, 0.0, 1.0].into()])
.unwrap() .unwrap()
.draw(pipeline.clone(), .draw(pipeline.clone(),
&dynamic_state, &dynamic_state,
@ -327,9 +307,33 @@ fn main() {
events_loop.poll_events(|ev| { events_loop.poll_events(|ev| {
match ev { match ev {
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true, winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true,
winit::Event::WindowEvent { event: winit::WindowEvent::Resized(_), .. } => recreate_swapchain = true,
_ => () _ => ()
} }
}); });
if done { return } if done { return }
} }
} }
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPassAbstract + Send + Sync>,
dynamic_state: &mut DynamicState
) -> Vec<Arc<FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
};
dynamic_state.viewports = Some(vec!(viewport));
images.iter().map(|image| {
Arc::new(
Framebuffer::start(render_pass.clone())
.add(image.clone()).unwrap()
.build().unwrap()
) as Arc<FramebufferAbstract + Send + Sync>
}).collect::<Vec<_>>()
}

View File

@ -32,27 +32,24 @@ extern crate vulkano_win;
use vulkano_win::VkSurfaceBuild; use vulkano_win::VkSurfaceBuild;
use vulkano::buffer::BufferUsage; use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::buffer::CpuAccessibleBuffer; use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
use vulkano::command_buffer::AutoCommandBufferBuilder;
use vulkano::command_buffer::DynamicState;
use vulkano::device::Device; use vulkano::device::Device;
use vulkano::framebuffer::Framebuffer; use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract};
use vulkano::framebuffer::Subpass; use vulkano::image::SwapchainImage;
use vulkano::instance::Instance; use vulkano::instance::Instance;
use vulkano::instance::PhysicalDevice;
use vulkano::pipeline::GraphicsPipeline; use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::viewport::Viewport; use vulkano::pipeline::viewport::Viewport;
use vulkano::swapchain::{AcquireError, PresentMode, SurfaceTransform, Swapchain, SwapchainCreationError};
use vulkano::swapchain; use vulkano::swapchain;
use vulkano::swapchain::PresentMode;
use vulkano::swapchain::SurfaceTransform;
use vulkano::swapchain::Swapchain;
use vulkano::swapchain::AcquireError;
use vulkano::swapchain::SwapchainCreationError;
use vulkano::sync::now;
use vulkano::sync::GpuFuture; use vulkano::sync::GpuFuture;
use vulkano::sync::now;
use vulkano_shaders::vulkano_shader; use vulkano_shaders::vulkano_shader;
use winit::Window;
use std::sync::Arc; use std::sync::Arc;
// TODO: Move this back to the middle of the example, it makes for a more coherent sequential explanation (check git history) // TODO: Move this back to the middle of the example, it makes for a more coherent sequential explanation (check git history)
@ -117,8 +114,7 @@ fn main() {
// //
// For the sake of the example we are just going to use the first device, which should work // For the sake of the example we are just going to use the first device, which should work
// most of the time. // most of the time.
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance) let physical = PhysicalDevice::enumerate(&instance).next().expect("no device available");
.next().expect("no device available");
// Some little debug infos. // Some little debug infos.
println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); println!("Using device: {} (type: {:?})", physical.name(), physical.ty());
@ -135,6 +131,7 @@ fn main() {
// window and a cross-platform Vulkan surface that represents the surface of the window. // window and a cross-platform Vulkan surface that represents the surface of the window.
let mut events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); let surface = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
let window = surface.window();
// The next step is to choose which GPU queue will execute our draw commands. // The next step is to choose which GPU queue will execute our draw commands.
// //
@ -185,21 +182,25 @@ fn main() {
// iterator and throw it away. // iterator and throw it away.
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
// The dimensions of the surface. // The dimensions of the window, only used to initially setup the swapchain.
// This variable needs to be mutable since the viewport can change size. let initial_dimensions = if let Some(dimensions) = window.get_inner_size() {
let mut dimensions; // convert to physical pixels
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
[dimensions.0, dimensions.1]
} else {
// The window no longer exists so exit the application.
return;
};
// Before we can draw on the surface, we have to create what is called a swapchain. Creating // Before we can draw on the surface, we have to create what is called a swapchain. Creating
// a swapchain allocates the color buffers that will contain the image that will ultimately // a swapchain allocates the color buffers that will contain the image that will ultimately
// be visible on the screen. These images are returned alongside with the swapchain. // be visible on the screen. These images are returned alongside with the swapchain.
let (mut swapchain, mut images) = { let (mut swapchain, images) = {
// Querying the capabilities of the surface. When we create the swapchain we can only // Querying the capabilities of the surface. When we create the swapchain we can only
// pass values that are allowed by the capabilities. // pass values that are allowed by the capabilities.
let caps = surface.capabilities(physical) let caps = surface.capabilities(physical)
.expect("failed to get surface capabilities"); .expect("failed to get surface capabilities");
dimensions = caps.current_extent.unwrap_or([1024, 768]);
// We choose the dimensions of the swapchain to match the current extent of the surface. // We choose the dimensions of the swapchain to match the current extent of the surface.
// If `caps.current_extent` is `None`, this means that the window size will be determined // If `caps.current_extent` is `None`, this means that the window size will be determined
// by the dimensions of the swapchain, in which case we just use the width and height defined above. // by the dimensions of the swapchain, in which case we just use the width and height defined above.
@ -213,7 +214,7 @@ fn main() {
// Please take a look at the docs for the meaning of the parameters we didn't mention. // Please take a look at the docs for the meaning of the parameters we didn't mention.
Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format, Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format,
dimensions, 1, caps.supported_usage_flags, &queue, initial_dimensions, 1, caps.supported_usage_flags, &queue,
SurfaceTransform::Identity, alpha, PresentMode::Fifo, true, SurfaceTransform::Identity, alpha, PresentMode::Fifo, true,
None).expect("failed to create swapchain") None).expect("failed to create swapchain")
}; };
@ -293,12 +294,16 @@ fn main() {
.build(device.clone()) .build(device.clone())
.unwrap()); .unwrap());
// Dynamic viewports allow us to recreate just the viewport when the window is resized
// Otherwise we would have to recreate the whole pipeline.
let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None };
// The render pass we created above only describes the layout of our framebuffers. Before we // The render pass we created above only describes the layout of our framebuffers. Before we
// can draw we also need to create the actual framebuffers. // can draw we also need to create the actual framebuffers.
// //
// Since we need to draw to multiple images, we are going to create a different framebuffer for // Since we need to draw to multiple images, we are going to create a different framebuffer for
// each image. // each image.
let mut framebuffers: Option<Vec<Arc<vulkano::framebuffer::Framebuffer<_,_>>>> = None; let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
// Initialization is finally finished! // Initialization is finally finished!
@ -321,16 +326,6 @@ fn main() {
// that, we store the submission of the previous frame here. // that, we store the submission of the previous frame here.
let mut previous_frame_end = Box::new(now(device.clone())) as Box<GpuFuture>; let mut previous_frame_end = Box::new(now(device.clone())) as Box<GpuFuture>;
let mut dynamic_state = DynamicState {
line_width: None,
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
};
loop { loop {
// It is important to call this function from time to time, otherwise resources will keep // It is important to call this function from time to time, otherwise resources will keep
// accumulating and you will eventually reach an out of memory error. // accumulating and you will eventually reach an out of memory error.
@ -338,47 +333,33 @@ fn main() {
// already processed, and frees the resources that are no longer needed. // already processed, and frees the resources that are no longer needed.
previous_frame_end.cleanup_finished(); previous_frame_end.cleanup_finished();
// If the swapchain needs to be recreated, recreate it // Whenever the window resizes we need to recreate everything dependent on the window size.
// In this example that includes the swapchain, the framebuffers and the dynamic state viewport.
if recreate_swapchain { if recreate_swapchain {
// Get the new dimensions for the viewport/framebuffers. // Get the new dimensions for the viewport/framebuffers.
dimensions = surface.capabilities(physical) let dimensions = if let Some(dimensions) = window.get_inner_size() {
.expect("failed to get surface capabilities") let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
.current_extent.unwrap(); [dimensions.0, dimensions.1]
} else {
return;
};
let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) { let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) {
Ok(r) => r, Ok(r) => r,
// This error tends to happen when the user is manually resizing the window. // This error tends to happen when the user is manually resizing the window.
// Simply restarting the loop is the easiest way to fix this issue. // Simply restarting the loop is the easiest way to fix this issue.
Err(SwapchainCreationError::UnsupportedDimensions) => { Err(SwapchainCreationError::UnsupportedDimensions) => continue,
continue;
},
Err(err) => panic!("{:?}", err) Err(err) => panic!("{:?}", err)
}; };
swapchain = new_swapchain; swapchain = new_swapchain;
images = new_images; // Because framebuffers contains an Arc on the old swapchain, we need to
// recreate framebuffers as well.
framebuffers = None; framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state);
dynamic_state.viewports = Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]);
recreate_swapchain = false; recreate_swapchain = false;
} }
// Because framebuffers contains an Arc on the old swapchain, we need to
// recreate framebuffers as well.
if framebuffers.is_none() {
framebuffers = Some(images.iter().map(|image| {
Arc::new(Framebuffer::start(render_pass.clone())
.add(image.clone()).unwrap()
.build().unwrap())
}).collect::<Vec<_>>());
}
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If // Before we can draw on the output, we have to *acquire* an image from the swapchain. If
// no image is available (which happens if you submit draw commands too quickly), then the // no image is available (which happens if you submit draw commands too quickly), then the
// function will block. // function will block.
@ -386,8 +367,7 @@ fn main() {
// //
// This function can block if no image is available. The parameter is an optional timeout // This function can block if no image is available. The parameter is an optional timeout
// after which the function call will return an error. // after which the function call will return an error.
let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), let (image_num, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), None) {
None) {
Ok(r) => r, Ok(r) => r,
Err(AcquireError::OutOfDate) => { Err(AcquireError::OutOfDate) => {
recreate_swapchain = true; recreate_swapchain = true;
@ -413,7 +393,7 @@ fn main() {
// The third parameter builds the list of values to clear the attachments with. The API // The third parameter builds the list of values to clear the attachments with. The API
// is similar to the list of attachments when building the framebuffers, except that // is similar to the list of attachments when building the framebuffers, except that
// only the attachments that use `load: Clear` appear in the list. // only the attachments that use `load: Clear` appear in the list.
.begin_render_pass(framebuffers.as_ref().unwrap()[image_num].clone(), false, .begin_render_pass(framebuffers[image_num].clone(), false,
vec![[0.0, 0.0, 1.0, 1.0].into()]) vec![[0.0, 0.0, 1.0, 1.0].into()])
.unwrap() .unwrap()
@ -475,9 +455,34 @@ fn main() {
events_loop.poll_events(|ev| { events_loop.poll_events(|ev| {
match ev { match ev {
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true, winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => done = true,
winit::Event::WindowEvent { event: winit::WindowEvent::Resized(_), .. } => recreate_swapchain = true,
_ => () _ => ()
} }
}); });
if done { return; } if done { return; }
} }
} }
/// This method is called once during initialization then again whenever the window is resized
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPassAbstract + Send + Sync>,
dynamic_state: &mut DynamicState
) -> Vec<Arc<FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
};
dynamic_state.viewports = Some(vec!(viewport));
images.iter().map(|image| {
Arc::new(
Framebuffer::start(render_pass.clone())
.add(image.clone()).unwrap()
.build().unwrap()
) as Arc<FramebufferAbstract + Send + Sync>
}).collect::<Vec<_>>()
}