// Any difference in code from vulkano triangle.rs example is noted with ALL CAPS COMMENT BLOCKS // The formatting is different though. // IMPORT use vulkano_text::{DrawText, DrawTextTrait}; // IMPORT END use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState, SubpassContents, CommandBufferUsage}; use vulkano::device::{Device, DeviceExtensions}; use vulkano::render_pass::{Framebuffer, FramebufferAbstract, Subpass, RenderPass}; use vulkano::image::SwapchainImage; use vulkano::image::view::ImageView; use vulkano::instance::{Instance, PhysicalDevice}; use vulkano::pipeline::GraphicsPipeline; use vulkano::pipeline::viewport::Viewport; use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError}; use vulkano::{swapchain, Version}; use vulkano::sync::{GpuFuture, FlushError}; use vulkano::sync; use vulkano_win::VkSurfaceBuild; use winit::window::{WindowBuilder, Window}; use winit::event_loop::{EventLoop, ControlFlow}; use winit::event::{Event, WindowEvent}; use std::sync::Arc; 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); }" } } fn main() { let required_extensions = vulkano_win::required_extensions(); let instance = Instance::new(None, Version::V1_2, &required_extensions, None).unwrap(); let physical = PhysicalDevice::enumerate(&instance).next().unwrap(); println!("Using device: {} (type: {:?})", physical.properties().device_name.as_ref().unwrap(), physical.properties().device_type.unwrap()); let event_loop = EventLoop::new(); let surface = WindowBuilder::new().build_vk_surface(&event_loop, instance.clone()).unwrap(); let queue_family = physical.queue_families().find(|&q| { q.supports_graphics() && surface.is_supported(q).unwrap_or(false) }).unwrap(); 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()).unwrap(); let queue = queues.next().unwrap(); let (mut swapchain, images) = { let caps = surface.capabilities(physical).unwrap(); let usage = caps.supported_usage_flags; let alpha = caps.supported_composite_alpha.iter().next().unwrap(); let format = caps.supported_formats[0].0; let dimensions: [u32; 2] = surface.window().inner_size().into(); Swapchain::start(device.clone(), surface.clone()) .num_images(caps.min_image_count) .format(format) .dimensions(dimensions) .usage(usage) .sharing_mode(&queue) .composite_alpha(alpha) .build() .unwrap() }; let vertex_buffer = { #[derive(Default, Debug, Clone)] struct Vertex { position: [f32; 2] } vulkano::impl_vertex!(Vertex, position); CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, [ // BIGGER TRIANGLE Vertex { position: [-1.0, 0.5] }, Vertex { position: [0.0, -1.0] }, Vertex { position: [0.5, 1.0] } // BIGGER TRIANGLE END ].iter().cloned()).unwrap() }; let vs = vs::Shader::load(device.clone()).unwrap(); let fs = fs::Shader::load(device.clone()).unwrap(); let render_pass = Arc::new(vulkano::single_pass_renderpass!( device.clone(), attachments: { color: { load: Clear, store: Store, format: swapchain.format(), samples: 1, } }, pass: { color: [color], depth_stencil: {} } ).unwrap()); let pipeline = Arc::new(GraphicsPipeline::start() .vertex_input_single_buffer() .vertex_shader(vs.main_entry_point(), ()) .triangle_list() .viewports_dynamic_scissors_irrelevant(1) .fragment_shader(fs.main_entry_point(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap()); // CREATE DRAWTEXT let mut draw_text = DrawText::new(device.clone(), queue.clone(), swapchain.clone(), &images); let (width, _): (u32, u32) = surface.window().inner_size().into(); let mut x = -200.0; // CREATE DRAWTEXT END let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None, compare_mask: None, write_mask: None, reference: None }; let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state); let mut recreate_swapchain = false; let mut previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box); event_loop.run(move |event, _, control_flow| { match event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { *control_flow = ControlFlow::Exit; }, Event::WindowEvent { event: WindowEvent::Resized(_), .. } => { recreate_swapchain = true; }, Event::RedrawEventsCleared => { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { let dimensions: [u32; 2] = surface.window().inner_size().into(); let (new_swapchain, new_images) = match swapchain.recreate().dimensions(dimensions).build() { Ok(r) => r, Err(SwapchainCreationError::UnsupportedDimensions) => return, Err(e) => panic!("Failed to recreate swapchain: {:?}", e) }; swapchain = new_swapchain; framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state); // RECREATE DRAWTEXT ON RESIZE draw_text = DrawText::new(device.clone(), queue.clone(), swapchain.clone(), &new_images); // RECREATE DRAWTEXT ON RESIZE END recreate_swapchain = false; } // SPECIFY TEXT TO DRAW if x > width as f32 { x = 0.0; } else { x += 0.4; } draw_text.queue_text(200.0, 50.0, 20.0, [1.0, 1.0, 1.0, 1.0], "The quick brown fox jumps over the lazy dog."); draw_text.queue_text(20.0, 200.0, 190.0, [0.0, 1.0, 1.0, 1.0], "Hello world!"); draw_text.queue_text(x, 350.0, 70.0, [0.51, 0.6, 0.74, 1.0], "Lenny: ( ͡° ͜ʖ ͡°)"); draw_text.queue_text(50.0, 350.0, 70.0, [1.0, 1.0, 1.0, 1.0], "Overlap"); // SPECIFY TEXT TO DRAW END let (image_num, suboptimal, acquire_future) = match swapchain::acquire_next_image(swapchain.clone(), None) { Ok(r) => r, Err(AcquireError::OutOfDate) => { recreate_swapchain = true; return; }, Err(e) => panic!("Failed to acquire next image: {:?}", e) }; if suboptimal { recreate_swapchain = true; } let clear_values = vec!([0.0, 0.0, 0.0, 1.0].into()); // CHANGED TO BLACK BACKGROUND let mut builder = AutoCommandBufferBuilder::primary(device.clone(), queue.family(), CommandBufferUsage::OneTimeSubmit).unwrap(); builder .begin_render_pass(framebuffers[image_num].clone(), SubpassContents::Inline, clear_values).unwrap() .draw(pipeline.clone(), &dynamic_state, vertex_buffer.clone(), (), (), vec![]).unwrap() .end_render_pass().unwrap() // DRAW THE TEXT .draw_text(&mut draw_text, image_num); // DRAW THE TEXT END 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_num) .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<_>); } } }, _ => () } }); } /// This method is called once during initialization, then again whenever the window is resized fn window_size_dependent_setup( images: &[Arc>], render_pass: Arc, dynamic_state: &mut DynamicState ) -> Vec> { 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| { let image_view = ImageView::new(image.clone()).unwrap(); Arc::new( Framebuffer::start(render_pass.clone()) .add(image_view).unwrap() .build().unwrap() ) as Arc }).collect::>() }