use std::sync::Arc;
use bytemuck::{Pod, Zeroable};
use vulkano::{
device::Device,
format::Format,
image::SampleCount,
pipeline::{
DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo,
graphics::{
GraphicsPipelineCreateInfo,
color_blend::{AttachmentBlend, ColorBlendAttachmentState, ColorBlendState},
input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
subpass::PipelineSubpassType,
vertex_input::{
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
VertexInputState,
},
viewport::ViewportState,
},
layout::PipelineDescriptorSetLayoutCreateInfo,
},
render_pass::Subpass,
shader::{ShaderModule, ShaderModuleCreateInfo, ShaderStages},
};
use aetna_core::paint::QuadInstance;
use crate::naga_compile::wgsl_to_spirv;
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)]
pub(crate) struct FrameUniforms {
pub viewport: [f32; 2],
pub time: f32,
pub scale_factor: f32,
}
fn vertex_input_state() -> VertexInputState {
let bind_vertex = VertexInputBindingDescription {
stride: (2 * std::mem::size_of::<f32>()) as u32,
input_rate: VertexInputRate::Vertex,
..Default::default()
};
let bind_instance = VertexInputBindingDescription {
stride: std::mem::size_of::<QuadInstance>() as u32,
input_rate: VertexInputRate::Instance { divisor: 1 },
..Default::default()
};
let attr = |binding: u32, offset: u32, format: Format| VertexInputAttributeDescription {
binding,
offset,
format,
..Default::default()
};
VertexInputState::new()
.binding(0, bind_vertex)
.binding(1, bind_instance)
.attribute(0, attr(0, 0, Format::R32G32_SFLOAT))
.attribute(1, attr(1, 0, Format::R32G32B32A32_SFLOAT))
.attribute(2, attr(1, 16, Format::R32G32B32A32_SFLOAT))
.attribute(3, attr(1, 32, Format::R32G32B32A32_SFLOAT))
.attribute(4, attr(1, 48, Format::R32G32B32A32_SFLOAT))
.attribute(5, attr(1, 64, Format::R32G32B32A32_SFLOAT))
.attribute(6, attr(1, 80, Format::R32G32B32A32_SFLOAT))
.attribute(7, attr(1, 96, Format::R32G32B32A32_SFLOAT))
}
pub(crate) fn build_shared_pipeline_layout(
device: Arc<Device>,
stages: &[PipelineShaderStageCreateInfo],
) -> Arc<PipelineLayout> {
let mut info = PipelineDescriptorSetLayoutCreateInfo::from_stages(stages);
if let Some(set0) = info.set_layouts.get_mut(0) {
for binding in set0.bindings.values_mut() {
binding.stages |= ShaderStages::VERTEX | ShaderStages::FRAGMENT;
}
}
PipelineLayout::new(
device.clone(),
info.into_pipeline_layout_create_info(device)
.expect("aetna-vulkano: pipeline layout from stages"),
)
.expect("aetna-vulkano: pipeline layout new")
}
pub(crate) fn multisample_state(sample_count: u32) -> MultisampleState {
let rasterization_samples = SampleCount::try_from(sample_count).unwrap_or(SampleCount::Sample1);
MultisampleState {
rasterization_samples,
sample_shading: if sample_count > 1 { Some(1.0) } else { None },
..Default::default()
}
}
pub(crate) fn build_quad_pipeline(
device: Arc<Device>,
subpass: Subpass,
sample_count: u32,
name: &str,
wgsl: &str,
) -> Arc<GraphicsPipeline> {
let words = wgsl_to_spirv(name, wgsl).unwrap_or_else(|e| panic!("WGSL compile failed: {e}"));
let module = unsafe {
ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&words))
.unwrap_or_else(|e| panic!("ShaderModule::new for `{name}`: {e}"))
};
let vs = module
.entry_point("vs_main")
.unwrap_or_else(|| panic!("`{name}` has no `vs_main` entry point"));
let fs = module
.entry_point("fs_main")
.unwrap_or_else(|| panic!("`{name}` has no `fs_main` entry point"));
let stages = [
PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs),
];
let layout = build_shared_pipeline_layout(device.clone(), &stages);
GraphicsPipeline::new(
device.clone(),
None,
GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(),
vertex_input_state: Some(vertex_input_state()),
input_assembly_state: Some(InputAssemblyState {
topology: PrimitiveTopology::TriangleStrip,
..Default::default()
}),
viewport_state: Some(ViewportState::default()),
rasterization_state: Some(RasterizationState::default()),
multisample_state: Some(multisample_state(sample_count)),
color_blend_state: Some(ColorBlendState::with_attachment_states(
subpass.num_color_attachments(),
ColorBlendAttachmentState {
blend: Some(AttachmentBlend::alpha()),
..Default::default()
},
)),
dynamic_state: [DynamicState::Viewport, DynamicState::Scissor]
.into_iter()
.collect(),
subpass: Some(PipelineSubpassType::BeginRenderPass(subpass)),
..GraphicsPipelineCreateInfo::layout(layout)
},
)
.unwrap_or_else(|e| panic!("GraphicsPipeline::new for `{name}`: {e:?}"))
}