asdf
This commit is contained in:
@@ -11,6 +11,4 @@ vulkano-win = "0.13"
|
||||
shade_runner = "0.1.2"
|
||||
shaderc = "0.5.0"
|
||||
cgmath = "0.17"
|
||||
image = "0.21"
|
||||
winit = "0.19"
|
||||
time = "0.1.37"
|
||||
|
||||
@@ -6,9 +6,12 @@ layout(location = 0) out vec3 pos;
|
||||
|
||||
layout(push_constant) uniform push_constants {
|
||||
float time;
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
} push;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position + vec3(sin(push.time / 10.), 0., 0.), 1.0);
|
||||
gl_Position = push.projection * push.view * push.model * vec4(position + vec3(sin(push.time / 10.), 0., 0.), 1.0);
|
||||
pos = gl_Position.xyz;
|
||||
}
|
||||
46
src/main.rs
46
src/main.rs
@@ -2,28 +2,36 @@ use crate::vulkan::{Vertex, GameData};
|
||||
use winit::{Event, WindowEvent, ElementState};
|
||||
use std::time::SystemTime;
|
||||
use std::iter::FromIterator;
|
||||
use cgmath::{Matrix4, SquareMatrix, Rad, Point3, Vector3};
|
||||
|
||||
mod vulkan;
|
||||
|
||||
const PRINT_KEYBOARD_INPUT: bool = false;
|
||||
|
||||
impl GameData<'_> {
|
||||
/// Returns true if event should be ignored by the vulkan handler
|
||||
fn on_window_event(self: &mut Self, event: &Event) -> bool {
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { device_id, input }, .. } => {
|
||||
let mods = String::from_iter(
|
||||
vec!["shift", "ctrl", "alt", "logo"].iter()
|
||||
.zip(vec![input.modifiers.shift, input.modifiers.ctrl, input.modifiers.alt, input.modifiers.logo])
|
||||
.filter(|(&_name, state)| *state)
|
||||
.map(|(&name, _state)| name));
|
||||
if mods.len() > 0 {
|
||||
println!("Keyboard {:?} input {:?} {:?} + {:?}", device_id, input.state, &mods, input.scancode)
|
||||
} else {
|
||||
println!("Keyboard {:?} input {:?} {:?}", device_id, input.state, input.scancode)
|
||||
if PRINT_KEYBOARD_INPUT {
|
||||
let mods = String::from_iter(
|
||||
vec!["shift", "ctrl", "alt", "logo"].iter()
|
||||
.zip(vec![input.modifiers.shift, input.modifiers.ctrl, input.modifiers.alt, input.modifiers.logo])
|
||||
.filter(|(&_name, state)| *state)
|
||||
.map(|(&name, _state)| name));
|
||||
if mods.len() > 0 {
|
||||
println!("Keyboard {:?} input {:?} {:?} + {:?}", device_id, input.state, &mods, input.scancode)
|
||||
} else {
|
||||
println!("Keyboard {:?} input {:?} {:?}", device_id, input.state, input.scancode)
|
||||
}
|
||||
}
|
||||
|
||||
if input.state == ElementState::Released && input.modifiers.ctrl && input.scancode == 19 {
|
||||
self.recreate_pipeline = true;
|
||||
}
|
||||
if input.state == ElementState::Released && input.scancode == 1 {
|
||||
self.shutdown = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -32,17 +40,29 @@ impl GameData<'_> {
|
||||
|
||||
fn update_push_constants(self: &mut Self) {
|
||||
self.push_constants.time = self.start_time.elapsed().unwrap().as_millis() as f32 / 1000.0;
|
||||
self.push_constants.model = Matrix4::identity();
|
||||
self.push_constants.view = Matrix4::look_at(Point3::new(f32::sin(self.push_constants.time), 0.0, f32::cos(self.push_constants.time)), Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
|
||||
self.push_constants.projection = cgmath::perspective(Rad(std::f32::consts::FRAC_PI_2), self.aspect_ratio, 0.01, 100.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PushConstants {
|
||||
pub time: f32
|
||||
pub time: f32,
|
||||
pub model: Matrix4<f32>,
|
||||
pub view: Matrix4<f32>,
|
||||
pub projection: Matrix4<f32>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut whatever = String::new();
|
||||
std::io::stdin().read_line(&mut whatever).unwrap();
|
||||
|
||||
let mut pc = PushConstants {
|
||||
time: 0.0
|
||||
time: 0.0,
|
||||
model: Matrix4::identity(),
|
||||
view: Matrix4::identity(),
|
||||
projection: Matrix4::identity()
|
||||
};
|
||||
|
||||
let data = GameData {
|
||||
@@ -57,7 +77,9 @@ fn main() {
|
||||
],
|
||||
push_constants: &mut pc,
|
||||
start_time: SystemTime::now(),
|
||||
recreate_pipeline: false
|
||||
recreate_pipeline: false,
|
||||
aspect_ratio: 1.0,
|
||||
shutdown: false,
|
||||
};
|
||||
|
||||
vulkan::init(data);
|
||||
|
||||
203
src/vulkan.rs
203
src/vulkan.rs
@@ -2,7 +2,7 @@ use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
|
||||
use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
|
||||
use vulkano::device::{Device, DeviceExtensions};
|
||||
use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract};
|
||||
use vulkano::image::SwapchainImage;
|
||||
use vulkano::image::{SwapchainImage, AttachmentImage};
|
||||
use vulkano::instance::{Instance, PhysicalDevice, ApplicationInfo, Version, InstanceExtensions};
|
||||
use vulkano::pipeline::{GraphicsPipeline};
|
||||
use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule};
|
||||
@@ -13,6 +13,7 @@ use vulkano::sync::{GpuFuture, FlushError};
|
||||
use vulkano::sync;
|
||||
use vulkano::pipeline::vertex::SingleBufferDefinition;
|
||||
use vulkano::descriptor::PipelineLayoutAbstract;
|
||||
use vulkano::format::Format;
|
||||
|
||||
use vulkano_win::VkSurfaceBuild;
|
||||
|
||||
@@ -51,6 +52,8 @@ pub struct GameData<'a> {
|
||||
pub line_vertices: Vec<Vertex>,
|
||||
pub push_constants: &'a mut PushConstants,
|
||||
pub recreate_pipeline: bool,
|
||||
pub aspect_ratio: f32,
|
||||
pub shutdown: bool,
|
||||
}
|
||||
|
||||
pub fn init(mut game: GameData) {
|
||||
@@ -130,44 +133,15 @@ pub fn init(mut game: GameData) {
|
||||
//
|
||||
// We have to choose which queues to use early on, because we will need this info very soon.
|
||||
let queue_family = physical.queue_families().find(|&q| {
|
||||
// We take the first queue that supports drawing to our window.
|
||||
q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
|
||||
}).unwrap();
|
||||
|
||||
// Now initializing the device. This is probably the most important object of Vulkan.
|
||||
//
|
||||
// We have to pass five parameters when creating a device:
|
||||
//
|
||||
// - Which physical device to connect to.
|
||||
//
|
||||
// - 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.
|
||||
//
|
||||
// - A list of layers to enable. This is very niche, and you will usually pass `None`.
|
||||
//
|
||||
// - The list of queues that we are going to use. The exact parameter is an iterator whose
|
||||
// items are `(Queue, f32)` where the floating-point represents the priority of the queue
|
||||
// between 0.0 and 1.0. The priority of the queue is a hint to the implementation about how
|
||||
// much it should prioritize queues between one another.
|
||||
//
|
||||
// The list of created queues is returned by the function alongside with the device.
|
||||
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();
|
||||
|
||||
// Since we can request multiple queues, the `queues` variable is in fact an iterator. In this
|
||||
// example we use only one queue, so we just retrieve the first and only element of the
|
||||
// iterator and throw it away.
|
||||
let queue = queues.next().unwrap();
|
||||
|
||||
// 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 with the swapchain.
|
||||
let (mut swapchain, images) = {
|
||||
// Querying the capabilities of the surface. When we create the swapchain we can only
|
||||
// pass values that are allowed by the capabilities.
|
||||
let caps = surface.capabilities(physical).unwrap();
|
||||
|
||||
let usage = caps.supported_usage_flags;
|
||||
@@ -202,7 +176,6 @@ pub fn init(mut game: GameData) {
|
||||
Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format,
|
||||
initial_dimensions, 1, usage, &queue, SurfaceTransform::Identity, alpha,
|
||||
PresentMode::Fifo, true, None).unwrap()
|
||||
|
||||
};
|
||||
|
||||
let mesh_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), game.mesh_vertices.iter().cloned()).unwrap();
|
||||
@@ -211,66 +184,29 @@ pub fn init(mut game: GameData) {
|
||||
let render_pass = Arc::new(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: <ty>` 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.format(),
|
||||
// TODO:
|
||||
samples: 1,
|
||||
},
|
||||
depth: {
|
||||
load: Clear,
|
||||
store: DontCare,
|
||||
format: Format::D16Unorm,
|
||||
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: {}
|
||||
depth_stencil: {depth}
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
|
||||
let mut pipeline = create_pipeline(device.clone(), sub_pass.clone()).unwrap();
|
||||
|
||||
let line_shader_vertex_entry;
|
||||
let line_shader_fragment_entry;
|
||||
let line_shader_module_vertex;
|
||||
let line_shader_module_fragment;
|
||||
let (line_shader, line_shader_data) = read_shader("shaders/line.vert", "shaders/line.frag").unwrap();
|
||||
unsafe {
|
||||
line_shader_module_vertex = ShaderModule::from_words(device.clone(), &line_shader.vertex).expect("Failed to load");
|
||||
line_shader_vertex_entry = line_shader_module_vertex.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
line_shader_data.vert_input,
|
||||
line_shader_data.vert_output,
|
||||
line_shader_data.vert_layout,
|
||||
GraphicsShaderType::Vertex);
|
||||
line_shader_module_fragment = ShaderModule::from_words(device.clone(), &line_shader.fragment).expect("Failed to load");
|
||||
line_shader_fragment_entry = line_shader_module_fragment.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
line_shader_data.frag_input,
|
||||
line_shader_data.frag_output,
|
||||
line_shader_data.frag_layout,
|
||||
GraphicsShaderType::Fragment);
|
||||
};
|
||||
|
||||
let line_pipeline = Arc::new(GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_shader(line_shader_vertex_entry.clone(), ())
|
||||
.line_list()
|
||||
.viewports_dynamic_scissors_irrelevant(1)
|
||||
.fragment_shader(line_shader_fragment_entry.clone(), ())
|
||||
.render_pass(sub_pass.clone())
|
||||
.build(device.clone())
|
||||
.unwrap());
|
||||
let mut pipeline = create_pipeline(device.clone(), sub_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false).unwrap();
|
||||
let line_pipeline = create_pipeline(device.clone(), sub_pass.clone(), "shaders/line.vert", "shaders/line.frag", true).unwrap();
|
||||
|
||||
// Dynamic viewports allow us to recreate just the viewport when the window is resized
|
||||
// Otherwise we would have to recreate the whole pipeline.
|
||||
@@ -281,7 +217,7 @@ pub fn init(mut game: GameData) {
|
||||
//
|
||||
// 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 dynamic_state);
|
||||
let mut framebuffers = window_size_dependent_setup(device.clone(), &images, render_pass.clone(), &mut dynamic_state, &mut game.aspect_ratio);
|
||||
|
||||
// In some situations, the swapchain will become invalid by it This includes for example
|
||||
// when the window is resized (as the images of the swapchain will no longer match the
|
||||
@@ -331,13 +267,13 @@ pub fn init(mut game: GameData) {
|
||||
swapchain = new_swapchain;
|
||||
// Because framebuffers contains an Arc on the old swapchain, we need to
|
||||
// recreate framebuffers as well.
|
||||
framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state);
|
||||
framebuffers = window_size_dependent_setup(device.clone(), &new_images, render_pass.clone(), &mut dynamic_state, &mut game.aspect_ratio);
|
||||
|
||||
recreate_swapchain = false;
|
||||
}
|
||||
|
||||
if game.recreate_pipeline {
|
||||
if let Some(pipeline_ok) = create_pipeline(device.clone(), sub_pass.clone()) {
|
||||
if let Some(pipeline_ok) = create_pipeline(device.clone(), sub_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false) {
|
||||
pipeline = pipeline_ok;
|
||||
println!("Updated pipeline.");
|
||||
} else {
|
||||
@@ -363,7 +299,7 @@ pub fn init(mut game: GameData) {
|
||||
};
|
||||
|
||||
// Specify the color to clear the framebuffer with i.e. blue
|
||||
let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into());
|
||||
let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into(), 1f32.into());
|
||||
|
||||
game.update_push_constants();
|
||||
|
||||
@@ -437,27 +373,23 @@ pub fn init(mut game: GameData) {
|
||||
|
||||
// Handling the window events in order to close the program when the user wants to close
|
||||
// it.
|
||||
let mut done = false;
|
||||
events_loop.poll_events(|ev| {
|
||||
if !game.on_window_event(&ev) {
|
||||
match ev {
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => done = true,
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => game.shutdown = true,
|
||||
Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
if done { return; }
|
||||
if game.shutdown { 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<dyn RenderPassAbstract + Send + Sync>,
|
||||
dynamic_state: &mut DynamicState
|
||||
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
|
||||
fn window_size_dependent_setup(device: Arc<Device>, images: &[Arc<SwapchainImage<Window>>], render_pass: Arc<dyn RenderPassAbstract + Send + Sync>, dynamic_state: &mut DynamicState, aspect_ratio: &mut f32) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
|
||||
let dimensions = images[0].dimensions();
|
||||
*aspect_ratio = dimensions[0] as f32 / dimensions[1] as f32;
|
||||
|
||||
let viewport = Viewport {
|
||||
origin: [0.0, 0.0],
|
||||
@@ -466,60 +398,71 @@ fn window_size_dependent_setup(
|
||||
};
|
||||
dynamic_state.viewports = Some(vec!(viewport));
|
||||
|
||||
let depth_buffer = AttachmentImage::transient(device.clone(), dimensions, Format::D16Unorm).unwrap();
|
||||
|
||||
images.iter().map(|image| {
|
||||
Arc::new(
|
||||
Framebuffer::start(render_pass.clone())
|
||||
.add(image.clone()).unwrap()
|
||||
.build().unwrap()
|
||||
Arc::new(Framebuffer::start(render_pass.clone())
|
||||
.add(image.clone()).unwrap()
|
||||
.add(depth_buffer.clone()).unwrap()
|
||||
.build().unwrap()
|
||||
) as Arc<dyn FramebufferAbstract + Send + Sync>
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn create_pipeline<T: RenderPassAbstract>(device: Arc<Device>, sub_pass: Subpass<Arc<T>>) -> Option<Arc<GraphicsPipeline<SingleBufferDefinition<Vertex>, Box<dyn PipelineLayoutAbstract + Send + Sync>, Arc<T>>>> {
|
||||
if let Some((mesh_shader, mesh_shader_data)) = read_shader("shaders/triangle.vert", "shaders/triangle.frag") {
|
||||
let mesh_shader_vertex_entry;
|
||||
let mesh_shader_fragment_entry;
|
||||
let mesh_shader_module_vertex;
|
||||
let mesh_shader_module_fragment;
|
||||
fn create_pipeline<T: RenderPassAbstract>(device: Arc<Device>, sub_pass: Subpass<Arc<T>>, vertex_shader_path: &str, fragment_shader_path: &str, is_line: bool) -> Option<Arc<GraphicsPipeline<SingleBufferDefinition<Vertex>, Box<dyn PipelineLayoutAbstract + Send + Sync>, Arc<T>>>> {
|
||||
if let Some((shader, shader_data)) = read_shader(vertex_shader_path, fragment_shader_path) {
|
||||
let vertex_shader_entry;
|
||||
let fragment_shader_entry;
|
||||
let vertex_shader_module;
|
||||
let fragment_shader_module;
|
||||
|
||||
unsafe {
|
||||
mesh_shader_module_vertex = ShaderModule::from_words(device.clone(), &mesh_shader.vertex).expect("Failed to load");
|
||||
mesh_shader_vertex_entry = mesh_shader_module_vertex.graphics_entry_point(
|
||||
vertex_shader_module = ShaderModule::from_words(device.clone(), &shader.vertex).expect("Failed to load vertex shader.");
|
||||
vertex_shader_entry = vertex_shader_module.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
mesh_shader_data.vert_input,
|
||||
mesh_shader_data.vert_output,
|
||||
mesh_shader_data.vert_layout,
|
||||
shader_data.vert_input,
|
||||
shader_data.vert_output,
|
||||
shader_data.vert_layout,
|
||||
GraphicsShaderType::Vertex);
|
||||
mesh_shader_module_fragment = ShaderModule::from_words(device.clone(), &mesh_shader.fragment).expect("Failed to load");
|
||||
mesh_shader_fragment_entry = mesh_shader_module_fragment.graphics_entry_point(
|
||||
fragment_shader_module = ShaderModule::from_words(device.clone(), &shader.fragment).expect("Failed to load fragment shader.");
|
||||
fragment_shader_entry = fragment_shader_module.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
mesh_shader_data.frag_input,
|
||||
mesh_shader_data.frag_output,
|
||||
mesh_shader_data.frag_layout,
|
||||
shader_data.frag_input,
|
||||
shader_data.frag_output,
|
||||
shader_data.frag_layout,
|
||||
GraphicsShaderType::Fragment);
|
||||
};
|
||||
|
||||
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
|
||||
// program, but much more specific.
|
||||
let pipeline = Arc::new(GraphicsPipeline::start()
|
||||
// We need to indicate the layout of the vertices.
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||
// which one. The `main` word of `main_entry_point` actually corresponds to the name of
|
||||
// the entry point.
|
||||
.vertex_shader(mesh_shader_vertex_entry.clone(), ())
|
||||
// The content of the vertex buffer describes a list of triangles.
|
||||
.triangle_list()
|
||||
// Use a resizable viewport set to draw over the entire window
|
||||
.viewports_dynamic_scissors_irrelevant(1)
|
||||
// See `vertex_shader`.
|
||||
.fragment_shader(mesh_shader_fragment_entry.clone(), ())
|
||||
// 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(sub_pass.clone())
|
||||
// Now that our builder is filled, we call `build()` to obtain an actual pipeline.
|
||||
.build(device.clone())
|
||||
.unwrap());
|
||||
let pipeline;
|
||||
if is_line {
|
||||
pipeline = Arc::new(GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_shader(vertex_shader_entry.clone(), ())
|
||||
.line_list()
|
||||
.viewports_dynamic_scissors_irrelevant(1)
|
||||
.fragment_shader(fragment_shader_entry.clone(), ())
|
||||
.render_pass(sub_pass.clone())
|
||||
.build(device.clone())
|
||||
.unwrap());
|
||||
} else {
|
||||
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
|
||||
// program, but much more specific.
|
||||
pipeline = Arc::new(GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||
// which one. The `main` word of `main_entry_point` actually corresponds to the name of
|
||||
// the entry point.
|
||||
.vertex_shader(vertex_shader_entry.clone(), ())
|
||||
.triangle_list()
|
||||
// Use a resizable viewport set to draw over the entire window
|
||||
.viewports_dynamic_scissors_irrelevant(1)
|
||||
.fragment_shader(fragment_shader_entry.clone(), ())
|
||||
// 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(sub_pass.clone())
|
||||
.build(device.clone())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
return Some(pipeline);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user