Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
340 lines (280 sloc) 12 KB
// We're going to spring for a tiny bit of code reuse, so we'll need to include the
// library crate.
extern crate gfx_hal_tutorials;
#[cfg(windows)]
extern crate gfx_backend_dx12 as backend;
#[cfg(target_os = "macos")]
extern crate gfx_backend_metal as backend;
#[cfg(all(unix, not(target_os = "macos")))]
extern crate gfx_backend_vulkan as backend;
extern crate gfx_hal;
extern crate winit;
// Saves us from having to import gfx types every time.
use gfx_hal_tutorials::prelude::*;
use winit::{Event, EventsLoop, KeyboardInput, VirtualKeyCode, WindowBuilder, WindowEvent};
fn main() {
let mut events_loop = EventsLoop::new();
let window = WindowBuilder::new()
.with_title("Part 01: Resizing")
.with_dimensions((256, 256).into())
.build(&events_loop)
.unwrap();
let instance = backend::Instance::create("Part 01: Resizing", 1);
let mut surface = instance.create_surface(&window);
let mut adapter = instance.enumerate_adapters().remove(0);
let (device, mut queue_group) = adapter
.open_with::<_, Graphics>(1, |family| surface.supports_queue_family(family))
.unwrap();
let mut command_pool =
device.create_command_pool_typed(&queue_group, CommandPoolCreateFlags::empty(), 16);
let physical_device = &adapter.physical_device;
// We don't need the capabilities just yet, since we use that for the swapchain.
let (_caps, formats, _) = surface.compatibility(physical_device);
// This could theoretically change between swapchain creations, but we're going
// to ignore that for now so that we only have to build our render pass and
// pipeline once.
let surface_color_format = {
match formats {
Some(choices) => choices
.into_iter()
.find(|format| format.base_format().1 == ChannelType::Srgb)
.unwrap(),
None => Format::Rgba8Srgb,
}
};
let render_pass = {
let color_attachment = Attachment {
format: Some(surface_color_format),
samples: 1,
ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store),
stencil_ops: AttachmentOps::DONT_CARE,
layouts: Layout::Undefined..Layout::Present,
};
let subpass = SubpassDesc {
colors: &[(0, Layout::ColorAttachmentOptimal)],
depth_stencil: None,
inputs: &[],
resolves: &[],
preserves: &[],
};
let dependency = SubpassDependency {
passes: SubpassRef::External..SubpassRef::Pass(0),
stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..PipelineStage::COLOR_ATTACHMENT_OUTPUT,
accesses: Access::empty()
..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE),
};
device.create_render_pass(&[color_attachment], &[subpass], &[dependency])
};
let pipeline_layout = device.create_pipeline_layout(&[], &[]);
let vertex_shader_module = {
let spirv = include_bytes!("../../assets/gen/shaders/part00.vert.spv");
device.create_shader_module(spirv).unwrap()
};
let fragment_shader_module = {
let spirv = include_bytes!("../../assets/gen/shaders/part00.frag.spv");
device.create_shader_module(spirv).unwrap()
};
let pipeline = {
let vs_entry = EntryPoint::<backend::Backend> {
entry: "main",
module: &vertex_shader_module,
specialization: Default::default(),
};
let fs_entry = EntryPoint::<backend::Backend> {
entry: "main",
module: &fragment_shader_module,
specialization: Default::default(),
};
let shader_entries = GraphicsShaderSet {
vertex: vs_entry,
hull: None,
domain: None,
geometry: None,
fragment: Some(fs_entry),
};
let subpass = Subpass {
index: 0,
main_pass: &render_pass,
};
let mut pipeline_desc = GraphicsPipelineDesc::new(
shader_entries,
Primitive::TriangleList,
Rasterizer::FILL,
&pipeline_layout,
subpass,
);
pipeline_desc
.blender
.targets
.push(ColorBlendDesc(ColorMask::ALL, BlendState::ALPHA));
device
.create_graphics_pipeline(&pipeline_desc, None)
.unwrap()
};
let frame_semaphore = device.create_semaphore();
let present_semaphore = device.create_semaphore();
// We're going to defer the construction of our swapchain, extent, image views,
// and framebuffers until the mainloop, because we will need to repeat it
// whenever the window resizes. For now we leave them empty.
//
// We're using an Option containing a tuple so that we can drop all four items
// together. We're also taking advantage of type inference as much as possible
// so we don't have to know the specific type names just yet.
let mut swapchain_stuff: Option<(_, _, _, _)> = None;
let mut rebuild_swapchain = false;
loop {
let mut quitting = false;
events_loop.poll_events(|event| {
if let Event::WindowEvent { event, .. } = event {
match event {
WindowEvent::CloseRequested => quitting = true,
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => quitting = true,
// We need to recreate our swapchain if we resize, so we'll set
// a flag when that happens.
WindowEvent::Resized(_) => {
rebuild_swapchain = true;
}
_ => {}
}
}
});
// We need to destroy things if we're resizing because we'll recreate them.
// We also need to destroy them if we're quitting, so we can clean them up.
if (rebuild_swapchain || quitting) && swapchain_stuff.is_some() {
// Take ownership over the old stuff so we can destroy it.
// The value of swapchain_stuff is now `None`.
let (swapchain, _extent, frame_views, framebuffers) = swapchain_stuff.take().unwrap();
// We want to wait for all queues to be idle and reset the command pool,
// so that we know that no commands are being executed while we destroy
// the swapchain.
device.wait_idle().unwrap();
command_pool.reset();
// Destroy all the old stuff.
for framebuffer in framebuffers {
device.destroy_framebuffer(framebuffer);
}
for image_view in frame_views {
device.destroy_image_view(image_view);
}
device.destroy_swapchain(swapchain);
}
if quitting {
// At this point, our swapchain is destroyed and will not be recreated.
break;
}
// If we don't have a swapchain here, we destroyed it and we need to
// recreate it.
if swapchain_stuff.is_none() {
rebuild_swapchain = false;
let (caps, _, _) = surface.compatibility(physical_device);
// Here we just create the swapchain, image views, and framebuffers
// like we did in part 00, and store them in swapchain_stuff.
let swap_config = SwapchainConfig::from_caps(&caps, surface_color_format);
let extent = swap_config.extent.to_extent();
let (swapchain, backbuffer) = device.create_swapchain(&mut surface, swap_config, None);
let (frame_views, framebuffers) = match backbuffer {
Backbuffer::Images(images) => {
let color_range = SubresourceRange {
aspects: Aspects::COLOR,
levels: 0..1,
layers: 0..1,
};
let image_views = images
.iter()
.map(|image| {
device
.create_image_view(
image,
ViewKind::D2,
surface_color_format,
Swizzle::NO,
color_range.clone(),
).unwrap()
}).collect::<Vec<_>>();
let fbos = image_views
.iter()
.map(|image_view| {
device
.create_framebuffer(&render_pass, vec![image_view], extent)
.unwrap()
}).collect();
(image_views, fbos)
}
Backbuffer::Framebuffer(fbo) => (Vec::new(), vec![fbo]),
};
// Store the new stuff.
swapchain_stuff = Some((swapchain, extent, frame_views, framebuffers));
}
// To access the swapchain, we need to get a mutable reference to the
// contents of swapchain_stuff. We know it's safe to unwrap because we just
// checked it wasn't `None`.
let (swapchain, extent, _frame_views, framebuffers) = swapchain_stuff.as_mut().unwrap();
// The rest of our rendering happens exactly the same way as before.
command_pool.reset();
let frame_index: SwapImageIndex = {
match swapchain.acquire_image(!0, FrameSync::Semaphore(&frame_semaphore)) {
Ok(i) => i,
Err(_) => {
rebuild_swapchain = true;
continue;
}
}
};
let finished_command_buffer = {
let mut command_buffer = command_pool.acquire_command_buffer(false);
let viewport = Viewport {
rect: Rect {
x: 0,
y: 0,
w: extent.width as i16,
h: extent.height as i16,
},
depth: 0.0..1.0,
};
command_buffer.set_viewports(0, &[viewport.clone()]);
command_buffer.set_scissors(0, &[viewport.rect]);
command_buffer.bind_graphics_pipeline(&pipeline);
{
let mut encoder = command_buffer.begin_render_pass_inline(
&render_pass,
&framebuffers[frame_index as usize],
viewport.rect,
&[ClearValue::Color(ClearColor::Float([0.0, 0.0, 0.0, 1.0]))],
);
encoder.draw(0..3, 0..1);
}
command_buffer.finish()
};
let submission = Submission::new()
.wait_on(&[(&frame_semaphore, PipelineStage::BOTTOM_OF_PIPE)])
.signal(&[&present_semaphore])
.submit(vec![finished_command_buffer]);
queue_group.queues[0].submit(submission, None);
let result = swapchain.present(
&mut queue_group.queues[0],
frame_index,
vec![&present_semaphore],
);
if result.is_err() {
rebuild_swapchain = true;
}
}
// Cleanup
// Note that we don't have to destroy the swapchain, frame images, or
// framebuffers, because they will already have been destroyed before breaking
// out of the mainloop.
device.destroy_graphics_pipeline(pipeline);
device.destroy_pipeline_layout(pipeline_layout);
device.destroy_render_pass(render_pass);
device.destroy_shader_module(vertex_shader_module);
device.destroy_shader_module(fragment_shader_module);
device.destroy_command_pool(command_pool.into_raw());
device.destroy_semaphore(frame_semaphore);
}