use std::sync::Arc; use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, device::Device, format::Format, memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter}, pipeline::{ graphics::{ color_blend::{ColorBlendAttachmentState, ColorBlendState}, input_assembly::InputAssemblyState, multisample::MultisampleState, rasterization::RasterizationState, subpass::PipelineRenderingCreateInfo, vertex_input::{Vertex, VertexDefinition}, viewport::ViewportState, GraphicsPipelineCreateInfo, }, layout::PipelineDescriptorSetLayoutCreateInfo, DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, }, }; pub fn test_pipeline( device: Arc, memory_allocator: Arc, image_format: Format, ) -> (Subbuffer<[MyVertex]>, Arc) { let vertices = [ MyVertex { position: [-0.5, -0.25], }, MyVertex { position: [0.0, 0.5], }, MyVertex { position: [0.25, -0.1], }, ]; let vertex_buffer = Buffer::from_iter( memory_allocator, BufferCreateInfo { usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, vertices, ) .unwrap(); let pipeline = { // First, we load the shaders that the pipeline will use: // the vertex shader and the fragment shader. // // A Vulkan shader can in theory contain multiple entry points, so we have to specify which // one. let vs = vs::load(device.clone()) .unwrap() .entry_point("main") .unwrap(); let fs = fs::load(device.clone()) .unwrap() .entry_point("main") .unwrap(); // Automatically generate a vertex input state from the vertex shader's input interface, // that takes a single vertex buffer containing `Vertex` structs. let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); // Make a list of the shader stages that the pipeline will have. let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; // We must now create a **pipeline layout** object, which describes the locations and types // of descriptor sets and push constants used by the shaders in the pipeline. // // Multiple pipelines can share a common layout object, which is more efficient. // The shaders in a pipeline must use a subset of the resources described in its pipeline // layout, but the pipeline layout is allowed to contain resources that are not present in // the shaders; they can be used by shaders in other pipelines that share the same // layout. Thus, it is a good idea to design shaders so that many pipelines have // common resource locations, which allows them to share pipeline layouts. let layout = PipelineLayout::new( device.clone(), // Since we only have one pipeline in this example, and thus one pipeline layout, // we automatically generate the creation info for it from the resources used in the // shaders. In a real application, you would specify this information manually so that // you can re-use one layout in multiple pipelines. PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) .into_pipeline_layout_create_info(device.clone()) .unwrap(), ) .unwrap(); // We describe the formats of attachment images where the colors, depth and/or stencil // information will be written. The pipeline will only be usable with this particular // configuration of the attachment images. let subpass = PipelineRenderingCreateInfo { // We specify a single color attachment that will be rendered to. When we begin // rendering, we will specify a swapchain image to be used as this attachment, so here // we set its format to be the same format as the swapchain. color_attachment_formats: vec![Some(image_format)], ..Default::default() }; // Finally, create the pipeline. GraphicsPipeline::new( device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), // How vertex data is read from the vertex buffers into the vertex shader. vertex_input_state: Some(vertex_input_state), // How vertices are arranged into primitive shapes. // The default primitive shape is a triangle. input_assembly_state: Some(InputAssemblyState::default()), // How primitives are transformed and clipped to fit the framebuffer. // We use a resizable viewport, set to draw over the entire window. viewport_state: Some(ViewportState::default()), // How polygons are culled and converted into a raster of pixels. // The default value does not perform any culling. rasterization_state: Some(RasterizationState::default()), // How multiple fragment shader samples are converted to a single pixel value. // The default value does not perform any multisampling. multisample_state: Some(MultisampleState::default()), // How pixel values are combined with the values already present in the framebuffer. // The default value overwrites the old value with the new one, without any // blending. color_blend_state: Some(ColorBlendState::with_attachment_states( subpass.color_attachment_formats.len() as u32, ColorBlendAttachmentState::default(), )), // Dynamic states allows us to specify parts of the pipeline settings when // recording the command buffer, before we perform drawing. // Here, we specify that the viewport should be dynamic. dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), ..GraphicsPipelineCreateInfo::layout(layout) }, ) .unwrap() }; (vertex_buffer, pipeline) } #[derive(BufferContents, Vertex)] #[repr(C)] pub struct MyVertex { #[format(R32G32_SFLOAT)] position: [f32; 2], } mod vs { vulkano_shaders::shader! { ty: "vertex", src: r" #version 450 layout(location = 0) in vec2 position; layout(location = 0) out vec3 fragColor; vec3 colors[3] = vec3[](vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0)); void main() { gl_Position = vec4(position, 0.0, 1.0); fragColor = colors[gl_VertexIndex]; } ", } } mod fs { vulkano_shaders::shader! { ty: "fragment", src: r" #version 450 layout(location = 0) in vec3 fragColor; layout(location = 0) out vec4 f_color; void main() { f_color = vec4(fragColor, 1.0); } ", } }