From 35083ee1485c08c9c4588cb11ef9b2a8ac1dc178 Mon Sep 17 00:00:00 2001 From: teridax Date: Thu, 6 Apr 2023 15:40:50 +0200 Subject: [PATCH] drawing a red quad --- .idea/.gitignore | 8 + .idea/eruption.iml | 12 ++ .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + Cargo.lock | 61 ++++++ Cargo.toml | 1 + src/lib.rs | 479 +++++++++++++++++++++++++++++++++++++++-- src/main.rs | 7 + src/shader/composit.rs | 25 +++ src/shader/mod.rs | 37 ++++ 11 files changed, 630 insertions(+), 20 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/eruption.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 src/main.rs create mode 100644 src/shader/composit.rs create mode 100644 src/shader/mod.rs diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/eruption.iml b/.idea/eruption.iml new file mode 100644 index 0000000..9b4cf84 --- /dev/null +++ b/.idea/eruption.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..639900d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3df7df4 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a6d5f85..10a594b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,6 +181,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -282,6 +291,7 @@ name = "eruption" version = "0.1.0" dependencies = [ "vulkano", + "vulkano-shaders", "vulkano-win", "winit", ] @@ -760,6 +770,15 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + [[package]] name = "ryu" version = "1.0.13" @@ -822,6 +841,27 @@ dependencies = [ "serde", ] +[[package]] +name = "shaderc" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31cef52787a0db5108788ea20bed13d6bf4b96287c5c5201e55725f7070f3443" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8f8439fffcffd6efcd74197204addf935dbab5752696bd990a6cd36d54cf64" +dependencies = [ + "cmake", + "libc", + "roxmltree", +] + [[package]] name = "slotmap" version = "1.0.6" @@ -1030,6 +1070,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "vulkano-shaders" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f8cf18e9becbc6d39f1c39e26bcf69546c93989553eb5748cd734a8a697a6e5" +dependencies = [ + "ahash", + "heck", + "proc-macro2", + "quote", + "shaderc", + "syn 1.0.109", + "vulkano", +] + [[package]] name = "vulkano-win" version = "0.33.0" @@ -1343,3 +1398,9 @@ name = "xml-rs" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" diff --git a/Cargo.toml b/Cargo.toml index dae3cb3..a26d022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ authors = ["Sven Vogel"] [dependencies] vulkano = "0.33.0" vulkano-win = "0.33.0" +vulkano-shaders = "0.33.0" winit = "0.28.3" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 1bfddf8..470cff1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,57 @@ -use vulkano::device::{DeviceExtensions, Properties, QueueFlags}; -use vulkano::device::physical::PhysicalDeviceType; +mod shader; + +use std::sync::Arc; +use vulkano::device::{Device, DeviceCreateInfo, DeviceExtensions, Properties, Queue, QueueCreateInfo, QueueFlags}; +use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType}; +use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage}; use vulkano::instance::{Instance, InstanceCreateInfo}; -use vulkano::VulkanLibrary; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}; +use vulkano::swapchain::{acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo}; +use vulkano::{sync, VulkanLibrary}; use vulkano_win::VkSurfaceBuild; -use winit::event_loop::EventLoop; -use winit::window::WindowBuilder; +use winit::event_loop::{ControlFlow, EventLoop}; +use winit::window::{Window, WindowBuilder}; +use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}; +use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; +use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents}; +use vulkano::image::view::ImageView; +use vulkano::pipeline::graphics::vertex_input::Vertex; +use vulkano::pipeline::graphics::viewport::Viewport; +use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass}; +use vulkano::sync::{FlushError, GpuFuture}; +use winit::event::{Event, WindowEvent}; + +// We now create a buffer that will store the shape of our triangle. We use `#[repr(C)]` here +// to force rustc to use a defined layout for our data, as the default representation has *no +// guarantees*. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct Vertex2d { + #[format(R32G32_SFLOAT)] + position: [f32; 2], +} + +const QUAD_VERTICES: [Vertex2d; 6] = [ + Vertex2d { + position: [-1.0, -1.0] + }, + Vertex2d { + position: [1.0, -1.0] + }, + Vertex2d { + position: [1.0, 1.0] + }, + + Vertex2d { + position: [-1.0, -1.0] + }, + Vertex2d { + position: [1.0, 1.0] + }, + Vertex2d { + position: [-1.0, 1.0] + } +]; pub fn init() { let lib = VulkanLibrary::new().unwrap(); @@ -38,6 +85,284 @@ pub fn init() { .build_vk_surface(&event_loop, instance.clone()) .unwrap(); + let (device, mut queues) = get_device(&instance, &surface); + + // Since we can request multiple queues, the `queues` variable is in fact an iterator. We only + // use one queue in this example, so we just retrieve the first and only element of the + // iterator. + let queue = queues.next().unwrap(); + + let (mut swapchain, images) = create_swapchain(&device, &surface); + + let memory_allocator = StandardMemoryAllocator::new_default(device.clone()); + + let vertex_buffer = Buffer::from_iter( + &memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + usage: MemoryUsage::Upload, + ..Default::default() + }, + QUAD_VERTICES, + ).unwrap(); + + // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL + // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this + // manually. + + let render_pass = create_render_pass(&device, &swapchain); + + // 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 viewport = Viewport { + origin: [0.0, 0.0], + dimensions: [0.0, 0.0], + depth_range: 0.0..1.0, + }; + + // 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. + // + // Since we need to draw to multiple images, we are going to create a different framebuffer for + // each image. + let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools + // underneath and provides a safe interface for them. + let command_buffer_allocator = + StandardCommandBufferAllocator::new(device.clone(), Default::default()); + + // Initialization is finally finished! + + // In some situations, the swapchain will become invalid by itself. This includes for example + // when the window is resized (as the images of the swapchain will no longer match the + // window's) or, on Android, when the application went to the background and goes back to the + // foreground. + // + // In this situation, acquiring a swapchain image or presenting it will return an error. + // Rendering to an image of that swapchain will not produce any error, but may or may not work. + // To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, + // we remember that we need to do this for the next loop iteration. + let mut recreate_swapchain = false; + + // In the loop below we are going to submit commands to the GPU. Submitting a command produces + // an object that implements the `GpuFuture` trait, which holds the resources for as long as + // they are in use by the GPU. + // + // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid + // that, we store the submission of the previous frame here. + let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + + let pipeline = shader::create_program(&render_pass, &device); + + 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 => { + // Do not draw the frame when the screen dimensions are zero. On Windows, this can + // occur when minimizing the application. + let window = surface.object().unwrap().downcast_ref::().unwrap(); + let dimensions = window.inner_size(); + if dimensions.width == 0 || dimensions.height == 0 { + return; + } + + // 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. + // Calling this function polls various fences in order to determine what the GPU + // has already processed, and frees the resources that are no longer needed. + previous_frame_end.as_mut().unwrap().cleanup_finished(); + + // 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 { + // Use the new dimensions of the window. + + let (new_swapchain, new_images) = + match swapchain.recreate(SwapchainCreateInfo { + image_extent: dimensions.into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + // 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. + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("failed to recreate swapchain: {e}"), + }; + + swapchain = new_swapchain; + + // Because framebuffers contains a reference to the old swapchain, we need to + // recreate framebuffers as well. + framebuffers = window_size_dependent_setup( + &new_images, + render_pass.clone(), + &mut viewport, + ); + + recreate_swapchain = false; + } + + // 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 function will block. This operation returns the index of + // the image that we are allowed to draw upon. + // + // This function can block if no image is available. The parameter is an optional + // timeout after which the function call will return an error. + let (image_index, suboptimal, acquire_future) = + match 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}"), + }; + + // `acquire_next_image` can be successful, but suboptimal. This means that the + // swapchain image will still work, but it may not display correctly. With some + // drivers this can be when the window resizes, but it may not cause the swapchain + // to become out of date. + if suboptimal { + recreate_swapchain = true; + } + + // In order to draw, we have to build a *command buffer*. The command buffer object + // holds the list of commands that are going to be executed. + // + // Building a command buffer is an expensive operation (usually a few hundred + // microseconds), but it is known to be a hot path in the driver and is expected to + // be optimized. + // + // Note that we have to pass a queue family when we create the command buffer. The + // command buffer will only be executable on that given queue family. + let mut builder = AutoCommandBufferBuilder::primary( + &command_buffer_allocator, + queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); + + builder + // Before we can draw, we have to *enter a render pass*. + .begin_render_pass( + RenderPassBeginInfo { + // A list of values to clear the attachments with. This list contains + // one item for each attachment in the render pass. In this case, there + // is only one attachment, and we clear it with a blue color. + // + // Only attachments that have `LoadOp::Clear` are provided with clear + // values, any others should use `ClearValue::None` as the clear value. + clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], + + ..RenderPassBeginInfo::framebuffer( + framebuffers[image_index as usize].clone(), + ) + }, + // The contents of the first (and only) subpass. This can be either + // `Inline` or `SecondaryCommandBuffers`. The latter is a bit more advanced + // and is not covered here. + SubpassContents::Inline, + ) + .unwrap() + // We are now inside the first subpass of the render pass. + .set_viewport(0, [viewport.clone()]) + .bind_pipeline_graphics(pipeline.clone()) + .bind_vertex_buffers(0, vertex_buffer.clone()) + // We add a draw command. + .draw(vertex_buffer.len() as u32, 1, 0, 0) + .unwrap() + // We leave the render pass. Note that if we had multiple subpasses we could + // have called `next_subpass` to jump to the next subpass. + .end_render_pass() + .unwrap(); + + // Finish building the command buffer by calling `build`. + let command_buffer = builder.build().unwrap(); + + let future = previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(queue.clone(), command_buffer) + .unwrap() + // The color output is now expected to contain our triangle. But in order to + // show it on the screen, we have to *present* the image by calling + // `then_swapchain_present`. + // + // This function does not actually present the image immediately. Instead it + // submits a present command at the end of the queue. This means that it will + // only be presented once the GPU has finished executing the command buffer + // that draws the triangle. + .then_swapchain_present( + queue.clone(), + SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + ) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + previous_frame_end = Some(future.boxed()); + } + Err(FlushError::OutOfDate) => { + recreate_swapchain = true; + previous_frame_end = Some(sync::now(device.clone()).boxed()); + } + Err(e) => { + panic!("failed to flush future: {e}"); + // previous_frame_end = Some(sync::now(device.clone()).boxed()); + } + } + } + _ => (), + } + }); +} + +/// This function is called once during initialization, then again whenever the window is resized. +fn window_size_dependent_setup( + images: &[Arc], + render_pass: Arc, + viewport: &mut Viewport, +) -> Vec> { + let dimensions = images[0].dimensions().width_height(); + viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32]; + + images + .iter() + .map(|image| { + let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() + }) + .collect::>() +} + +fn get_device(instance: &Arc, surface: &Arc) -> (Arc, impl ExactSizeIterator> + Sized) { // Choose device extensions that we're going to use. In order to present images to a surface, // we need a `Swapchain`, which is provided by the `khr_swapchain` extension. let device_extensions = DeviceExtensions { @@ -45,9 +370,75 @@ pub fn init() { ..DeviceExtensions::empty() }; + let (physical_device, queue_family_index) = choose_physical_device(&instance, &surface, device_extensions); + + print_physical_device_info(physical_device.properties()); + + // Now initializing the device. This is probably the most important object of Vulkan. + // + // An iterator of created queues is returned by the function alongside the device. + Device::new( + // Which physical device to connect to. + physical_device, + DeviceCreateInfo { + // A list of optional features and extensions that our program needs to work correctly. + // Some parts of the Vulkan specs are optional and must be enabled manually at device + // creation. In this example the only thing we are going to need is the `khr_swapchain` + // extension that allows us to draw to a window. + enabled_extensions: device_extensions, + + // The list of queues that we are going to use. Here we only use one queue, from the + // previously chosen queue family. + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + + ..Default::default() + }, + ).unwrap() +} + +fn create_render_pass(device: &Arc, swapchain: &Arc) -> Arc { + // The next step is to create a *render pass*, which is an object that describes where the + // output of the graphics pipeline will go. It describes the layout of the images where the + // colors, depth and/or stencil information will be written. + vulkano::single_pass_renderpass!( + device.clone(), + attachments: { + // `color` is a custom name we give to the first and only attachment. + color: { + // `load: Clear` means that we ask the GPU to clear the content of this attachment + // at the start of the drawing. + load: Clear, + // `store: Store` means that we ask the GPU to store the output of the draw in the + // actual image. We could also ask it to discard the result. + store: Store, + // `format: ` indicates the type of the format of the image. This has to be one + // of the types of the `vulkano::format` module (or alternatively one of your + // structs that implements the `FormatDesc` trait). Here we use the same format as + // the swapchain. + format: swapchain.image_format(), + // `samples: 1` means that we ask the GPU to use one sample to determine the value + // of each pixel in the color attachment. We could use a larger value + // (multisampling) for antialiasing. An example of this can be found in + // msaa-renderpass.rs. + samples: 1, + }, + }, + pass: { + // We use the attachment named `color` as the one and only color attachment. + color: [color], + // No depth-stencil attachment is indicated with empty brackets. + depth_stencil: {}, + }, + ).unwrap() +} + +fn choose_physical_device(instance: &Arc, surface: &Arc, device_extensions: DeviceExtensions) -> (Arc, u32) { // We then choose which physical device to use. First, we enumerate all the available physical // devices, then apply filters to narrow them down to those that can support our needs. - let (physical_device, queue_family_index) = instance + instance .enumerate_physical_devices() .unwrap() .filter(|p| { @@ -102,9 +493,67 @@ pub fn init() { _ => 5, } }) - .expect("no suitable physical device found"); + .expect("no suitable physical device found") +} - print_physical_device_info(physical_device.properties()); +fn create_swapchain(device: &Arc, surface: &Arc) -> (Arc, Vec>) { + // 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 be + // visible on the screen. These images are returned alongside the swapchain. + // Querying the capabilities of the surface. When we create the swapchain we can only pass + // values that are allowed by the capabilities. + let surface_capabilities = device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + // Choosing the internal format that the images will have. + let image_format = Some( + device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); + let window = surface.object().unwrap().downcast_ref::().unwrap(); + + // Please take a look at the docs for the meaning of the parameters we didn't mention. + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + + image_format, + + // The dimensions of the window, only used to initially setup the swapchain. + // + // NOTE: + // On some drivers the swapchain dimensions are specified by + // `surface_capabilities.current_extent` and the swapchain size must use these + // dimensions. These dimensions are always the same as the window dimensions. + // + // However, other drivers don't specify a value, i.e. + // `surface_capabilities.current_extent` is `None`. These drivers will allow + // anything, but the only sensible value is the window dimensions. + // + // Both of these cases need the swapchain to use the window dimensions, so we just + // use that. + image_extent: window.inner_size().into(), + + image_usage: ImageUsage::COLOR_ATTACHMENT, + + // The alpha mode indicates how the alpha value of the final image will behave. For + // example, you can choose whether the window will be opaque or transparent. + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ).unwrap() } fn print_physical_device_info(device_properties: &Properties) { @@ -120,15 +569,5 @@ fn print_device_driver_info(device_properties: &Properties) { let default_info = String::from(""); let info = device_properties.driver_info.as_ref().unwrap_or(&default_info); - format!("driver:\n\tname: {}\n\tversion: {}\n\t info: {}\n", name, device_properties.driver_version, info); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - init(); - } -} + println!("driver:\n\tname: {}\n\tversion: {}\n\tinfo: {}\n", name, device_properties.driver_version, info); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..18c3665 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,7 @@ +use eruption::init; + +fn main() { + init(); + + +} \ No newline at end of file diff --git a/src/shader/composit.rs b/src/shader/composit.rs new file mode 100644 index 0000000..6b34326 --- /dev/null +++ b/src/shader/composit.rs @@ -0,0 +1,25 @@ +pub(crate) mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + layout(location = 0) in vec2 position; + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } +} + +pub(crate) mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + layout(location = 0) out vec4 f_color; + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } +} \ No newline at end of file diff --git a/src/shader/mod.rs b/src/shader/mod.rs new file mode 100644 index 0000000..b6c03fc --- /dev/null +++ b/src/shader/mod.rs @@ -0,0 +1,37 @@ +mod composit; + +use std::sync::Arc; +use vulkano::device::Device; +use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; +use vulkano::pipeline::graphics::vertex_input::Vertex; +use vulkano::pipeline::graphics::viewport::ViewportState; +use vulkano::pipeline::GraphicsPipeline; +use vulkano::render_pass::{RenderPass, Subpass}; +use crate::Vertex2d; + +pub fn create_program(render_pass: &Arc, device: &Arc) -> Arc { + + let vs = composit::vs::load(device.clone()).unwrap(); + let fs = composit::fs::load(device.clone()).unwrap(); + + // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL + // program, but much more specific. + GraphicsPipeline::start() + // We have to indicate which subpass of which render pass this pipeline is going to be used + // in. The pipeline will only be usable from this particular subpass. + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + // We need to indicate the layout of the vertices. + .vertex_input_state(Vertex2d::per_vertex()) + // The content of the vertex buffer describes a list of triangles. + .input_assembly_state(InputAssemblyState::new()) + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. + .vertex_shader(vs.entry_point("main").unwrap(), ()) + // Use a resizable viewport set to draw over the entire window + .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) + // See `vertex_shader`. + .fragment_shader(fs.entry_point("main").unwrap(), ()) + // Now that our builder is filled, we call `build()` to obtain an actual pipeline. + .build(device.clone()) + .unwrap() +} \ No newline at end of file