891 lines
29 KiB
Rust
891 lines
29 KiB
Rust
use std::{borrow::Cow, path::Path, sync::Arc};
|
|
|
|
use ash::{ext, prelude::*, vk};
|
|
use parking_lot::Mutex;
|
|
|
|
use crate::{
|
|
device::{
|
|
Device, DeviceHandle, DeviceInner, DeviceObject, asdf::traits::ExternallyManagedObject,
|
|
},
|
|
make_extension,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub struct ShaderStageDesc<'a> {
|
|
pub flags: vk::PipelineShaderStageCreateFlags,
|
|
pub module: &'a ShaderModule,
|
|
pub stage: vk::ShaderStageFlags,
|
|
pub entry: Cow<'a, std::ffi::CStr>,
|
|
// specialization: Option<vk::SpecializationInfo>
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct DescriptorSetLayoutBindingDesc {
|
|
pub binding: u32,
|
|
pub count: u32,
|
|
pub kind: vk::DescriptorType,
|
|
pub stage: vk::ShaderStageFlags,
|
|
pub flags: Option<vk::DescriptorBindingFlags>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct DescriptorSetLayoutDesc<'a> {
|
|
pub flags: vk::DescriptorSetLayoutCreateFlags,
|
|
pub bindings: &'a [DescriptorSetLayoutBindingDesc],
|
|
pub name: Option<Cow<'static, str>>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct PipelineLayoutDesc<'a> {
|
|
pub descriptor_set_layouts: &'a [&'a DescriptorSetLayout],
|
|
pub push_constant_ranges: &'a [vk::PushConstantRange],
|
|
pub name: Option<Cow<'static, str>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ComputePipelineDesc<'a> {
|
|
pub flags: vk::PipelineCreateFlags,
|
|
pub name: Option<Cow<'static, str>>,
|
|
pub shader_stage: ShaderStageDesc<'a>,
|
|
pub layout: Arc<PipelineLayout>,
|
|
pub base_pipeline: Option<Arc<Pipeline>>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct VertexInputState<'a> {
|
|
// pub flags: vk::PipelineVertexInputStateCreateFlags,
|
|
pub bindings: &'a [vk::VertexInputBindingDescription],
|
|
pub attributes: &'a [vk::VertexInputAttributeDescription],
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct TessellationState {
|
|
pub flags: vk::PipelineTessellationStateCreateFlags,
|
|
pub patch_control_points: u32,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct InputAssemblyState {
|
|
// pub flags: vk::PipelineInputAssemblyStateCreateFlags,
|
|
pub topology: vk::PrimitiveTopology,
|
|
pub primitive_restart: bool,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct ViewportState<'a> {
|
|
pub num_scissors: u32,
|
|
pub scissors: Option<&'a [vk::Rect2D]>,
|
|
pub num_viewports: u32,
|
|
pub viewports: Option<&'a [vk::Viewport]>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct DepthBiasState {
|
|
pub clamp: f32,
|
|
pub constant_factor: f32,
|
|
pub slope_factor: f32,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct RasterizationState {
|
|
pub depth_clamp_enable: bool,
|
|
pub discard_enable: bool,
|
|
pub line_width: f32,
|
|
pub cull_mode: vk::CullModeFlags,
|
|
pub depth_bias: Option<DepthBiasState>,
|
|
pub polygon_mode: vk::PolygonMode,
|
|
}
|
|
|
|
impl Default for RasterizationState {
|
|
fn default() -> Self {
|
|
Self {
|
|
depth_clamp_enable: false,
|
|
line_width: 1.0,
|
|
cull_mode: vk::CullModeFlags::BACK,
|
|
depth_bias: Default::default(),
|
|
polygon_mode: vk::PolygonMode::FILL,
|
|
discard_enable: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MultisampleState<'a> {
|
|
pub flags: vk::PipelineMultisampleStateCreateFlags,
|
|
pub sample_shading_enable: bool,
|
|
pub rasterization_samples: vk::SampleCountFlags,
|
|
pub min_sample_shading: f32,
|
|
pub sample_mask: &'a [vk::SampleMask],
|
|
pub alpha_to_coverage_enable: bool,
|
|
pub alpha_to_one_enable: bool,
|
|
}
|
|
|
|
impl<'a> Default for MultisampleState<'a> {
|
|
fn default() -> Self {
|
|
Self {
|
|
flags: Default::default(),
|
|
sample_shading_enable: Default::default(),
|
|
rasterization_samples: vk::SampleCountFlags::TYPE_1,
|
|
min_sample_shading: 1.0,
|
|
sample_mask: Default::default(),
|
|
alpha_to_coverage_enable: Default::default(),
|
|
alpha_to_one_enable: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DepthBounds {
|
|
pub min: f32,
|
|
pub max: f32,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct DepthState {
|
|
pub write_enable: bool,
|
|
/// sets depthTestEnable to true when `Some`
|
|
pub compare_op: Option<vk::CompareOp>,
|
|
/// sets depthBoundsTestEnable to true when `Some`
|
|
pub bounds: Option<DepthBounds>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct StencilState {
|
|
pub front: vk::StencilOpState,
|
|
pub back: vk::StencilOpState,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct DepthStencilState {
|
|
pub flags: vk::PipelineDepthStencilStateCreateFlags,
|
|
pub depth: Option<DepthState>,
|
|
pub stencil: Option<StencilState>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct ColorBlendState<'a> {
|
|
pub flags: vk::PipelineColorBlendStateCreateFlags,
|
|
pub attachments: &'a [vk::PipelineColorBlendAttachmentState],
|
|
pub logic_op: Option<vk::LogicOp>,
|
|
pub blend_constants: [f32; 4],
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct RenderingState<'a> {
|
|
pub color_formats: &'a [vk::Format],
|
|
pub depth_format: Option<vk::Format>,
|
|
pub stencil_format: Option<vk::Format>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct DynamicState<'a> {
|
|
pub flags: vk::PipelineDynamicStateCreateFlags,
|
|
pub dynamic_states: &'a [vk::DynamicState],
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct GraphicsPipelineDesc<'a> {
|
|
pub flags: vk::PipelineCreateFlags,
|
|
pub name: Option<Cow<'static, str>>,
|
|
pub shader_stages: &'a [ShaderStageDesc<'a>],
|
|
pub render_pass: Option<vk::RenderPass>,
|
|
pub layout: &'a PipelineLayout,
|
|
pub subpass: Option<u32>,
|
|
pub base_pipeline: Option<Arc<Pipeline>>,
|
|
|
|
pub vertex_input: Option<VertexInputState<'a>>,
|
|
pub input_assembly: Option<InputAssemblyState>,
|
|
pub tessellation: Option<TessellationState>,
|
|
pub viewport: Option<ViewportState<'a>>,
|
|
pub rasterization: Option<RasterizationState>,
|
|
pub multisample: Option<MultisampleState<'a>>,
|
|
pub depth_stencil: Option<DepthStencilState>,
|
|
pub color_blend: Option<ColorBlendState<'a>>,
|
|
pub dynamic: Option<DynamicState<'a>>,
|
|
pub rendering: Option<RenderingState<'a>>,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct DescriptorPoolDesc<'a> {
|
|
pub flags: vk::DescriptorPoolCreateFlags,
|
|
pub name: Option<Cow<'static, str>>,
|
|
pub sizes: &'a [vk::DescriptorPoolSize],
|
|
pub max_sets: u32,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DescriptorSetAllocDesc<'a> {
|
|
pub name: Option<Cow<'static, str>>,
|
|
pub layout: &'a DescriptorSetLayout,
|
|
}
|
|
|
|
impl DeviceHandle for vk::DescriptorPool {
|
|
unsafe fn destroy(&mut self, device: &Device) {
|
|
unsafe { device.dev().destroy_descriptor_pool(*self, None) };
|
|
}
|
|
}
|
|
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::DescriptorPool {
|
|
unsafe fn destroy(self, device: &T) {
|
|
// SAFETY: We have exclusive ownership of the descriptor pool, so it's safe to destroy it.
|
|
unsafe {
|
|
device.as_ref().raw.destroy_descriptor_pool(self, None);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DescriptorPool {
|
|
pool: DeviceObject<vk::DescriptorPool>,
|
|
lock: Mutex<()>,
|
|
}
|
|
|
|
impl DescriptorPool {
|
|
pub fn new(device: Device, desc: DescriptorPoolDesc) -> crate::Result<Self> {
|
|
let info = &vk::DescriptorPoolCreateInfo::default()
|
|
.flags(desc.flags)
|
|
.max_sets(desc.max_sets)
|
|
.pool_sizes(desc.sizes);
|
|
|
|
let handle = unsafe { device.dev().create_descriptor_pool(info, None)? };
|
|
|
|
Ok(Self {
|
|
pool: DeviceObject::new_debug_named(device, handle, desc.name),
|
|
lock: Mutex::new(()),
|
|
})
|
|
}
|
|
|
|
pub fn allocate(&self, descs: &[DescriptorSetAllocDesc]) -> VkResult<Vec<vk::DescriptorSet>> {
|
|
let layouts = descs
|
|
.iter()
|
|
.map(|desc| desc.layout.raw())
|
|
.collect::<Vec<_>>();
|
|
|
|
let info = &vk::DescriptorSetAllocateInfo::default()
|
|
.descriptor_pool(*self.pool)
|
|
.set_layouts(&layouts);
|
|
|
|
let sets = unsafe {
|
|
let _lock = self.lock.lock();
|
|
self.pool.device().raw.allocate_descriptor_sets(info)?
|
|
};
|
|
|
|
for (&set, desc) in sets.iter().zip(descs) {
|
|
if let Some(name) = desc.name.as_ref() {
|
|
unsafe { self.pool.device().debug_name_object(set, name) };
|
|
}
|
|
}
|
|
|
|
Ok(sets)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn reset(&self) -> VkResult<()> {
|
|
let _lock = self.lock.lock();
|
|
unsafe {
|
|
self.pool
|
|
.device()
|
|
.raw
|
|
.reset_descriptor_pool(*self.pool, vk::DescriptorPoolResetFlags::empty())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::DescriptorSetLayout {
|
|
unsafe fn destroy(self, device: &T) {
|
|
unsafe {
|
|
device
|
|
.as_ref()
|
|
.raw
|
|
.destroy_descriptor_set_layout(self, None);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DescriptorSetLayout {
|
|
layout: DeviceObject<vk::DescriptorSetLayout>,
|
|
}
|
|
|
|
impl DescriptorSetLayout {
|
|
pub fn new(device: Device, desc: DescriptorSetLayoutDesc) -> crate::Result<Self> {
|
|
let (flags, bindings): (Vec<_>, Vec<_>) = desc
|
|
.bindings
|
|
.iter()
|
|
.map(|binding| {
|
|
let flag = binding.flags.unwrap_or_default();
|
|
let binding = vk::DescriptorSetLayoutBinding::default()
|
|
.binding(binding.binding)
|
|
.descriptor_count(binding.count)
|
|
.descriptor_type(binding.kind)
|
|
.stage_flags(binding.stage);
|
|
|
|
(flag, binding)
|
|
})
|
|
.unzip();
|
|
|
|
let flags =
|
|
&mut vk::DescriptorSetLayoutBindingFlagsCreateInfo::default().binding_flags(&flags);
|
|
|
|
let mut info = vk::DescriptorSetLayoutCreateInfo::default()
|
|
.bindings(&bindings)
|
|
.flags(desc.flags);
|
|
|
|
if device.properties().device_api_version >= vk::API_VERSION_1_2
|
|
|| device
|
|
.properties()
|
|
.supports_extension(make_extension!(ext::descriptor_indexing))
|
|
{
|
|
info = info.push_next(flags);
|
|
}
|
|
|
|
let layout = unsafe { device.raw.create_descriptor_set_layout(&info, None)? };
|
|
|
|
Ok(Self {
|
|
layout: DeviceObject::new_debug_named(device, layout, desc.name),
|
|
})
|
|
}
|
|
|
|
pub fn raw(&self) -> vk::DescriptorSetLayout {
|
|
*self.layout
|
|
}
|
|
}
|
|
|
|
use crate::device::DeviceOwned;
|
|
|
|
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::PipelineLayout {
|
|
unsafe fn destroy(self, device: &T) {
|
|
unsafe {
|
|
device.as_ref().raw.destroy_pipeline_layout(self, None);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct PipelineLayout {
|
|
layout: DeviceObject<vk::PipelineLayout>,
|
|
}
|
|
|
|
impl PipelineLayout {
|
|
pub fn new(device: Device, desc: PipelineLayoutDesc) -> crate::Result<Self> {
|
|
let set_layouts = desc
|
|
.descriptor_set_layouts
|
|
.iter()
|
|
.map(|desc| desc.raw())
|
|
.collect::<Vec<_>>();
|
|
let info = &vk::PipelineLayoutCreateInfo::default()
|
|
.set_layouts(&set_layouts)
|
|
.push_constant_ranges(desc.push_constant_ranges);
|
|
let layout = unsafe { device.raw.create_pipeline_layout(info, None)? };
|
|
|
|
Ok(Self {
|
|
layout: DeviceObject::new_debug_named(device, layout, desc.name),
|
|
})
|
|
}
|
|
|
|
pub fn raw(&self) -> vk::PipelineLayout {
|
|
*self.layout
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct SamplerDesc {
|
|
pub flags: vk::SamplerCreateFlags,
|
|
pub min_filter: vk::Filter,
|
|
pub mag_filter: vk::Filter,
|
|
pub mipmap_mode: vk::SamplerMipmapMode,
|
|
pub address_u: vk::SamplerAddressMode,
|
|
pub address_v: vk::SamplerAddressMode,
|
|
pub address_w: vk::SamplerAddressMode,
|
|
pub mip_lod_bias: f32,
|
|
pub anisotropy_enable: bool,
|
|
pub max_anisotropy: f32,
|
|
pub compare_op: Option<vk::CompareOp>,
|
|
pub min_lod: f32,
|
|
pub max_lod: f32,
|
|
pub border_color: vk::BorderColor,
|
|
pub unnormalized_coordinates: bool,
|
|
}
|
|
|
|
impl Eq for SamplerDesc {}
|
|
impl PartialEq for SamplerDesc {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
use crate::util::eq_f32;
|
|
self.flags == other.flags
|
|
&& self.min_filter == other.min_filter
|
|
&& self.mag_filter == other.mag_filter
|
|
&& self.mipmap_mode == other.mipmap_mode
|
|
&& self.address_u == other.address_u
|
|
&& self.address_v == other.address_v
|
|
&& self.address_w == other.address_w
|
|
&& self.anisotropy_enable == other.anisotropy_enable
|
|
&& self.compare_op == other.compare_op
|
|
&& eq_f32(self.mip_lod_bias, other.mip_lod_bias)
|
|
&& eq_f32(self.max_anisotropy, other.max_anisotropy)
|
|
&& eq_f32(self.min_lod, other.min_lod)
|
|
&& eq_f32(self.max_lod, other.max_lod)
|
|
&& self.border_color == other.border_color
|
|
&& self.unnormalized_coordinates == other.unnormalized_coordinates
|
|
}
|
|
}
|
|
|
|
impl std::hash::Hash for SamplerDesc {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
use crate::util::hash_f32;
|
|
self.flags.hash(state);
|
|
self.min_filter.hash(state);
|
|
self.mag_filter.hash(state);
|
|
self.mipmap_mode.hash(state);
|
|
self.address_u.hash(state);
|
|
self.address_v.hash(state);
|
|
self.address_w.hash(state);
|
|
hash_f32(state, self.mip_lod_bias);
|
|
hash_f32(state, self.max_anisotropy);
|
|
hash_f32(state, self.min_lod);
|
|
hash_f32(state, self.max_lod);
|
|
self.anisotropy_enable.hash(state);
|
|
self.compare_op.hash(state);
|
|
self.border_color.hash(state);
|
|
self.unnormalized_coordinates.hash(state);
|
|
}
|
|
}
|
|
|
|
impl<T> ExternallyManagedObject<T> for vk::Sampler
|
|
where
|
|
T: AsRef<DeviceInner>,
|
|
{
|
|
unsafe fn destroy(self, device: &T) {
|
|
unsafe {
|
|
device.as_ref().raw.destroy_sampler(self, None);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Sampler {
|
|
sampler: DeviceObject<vk::Sampler>,
|
|
}
|
|
|
|
impl Sampler {
|
|
pub fn new(device: Device, desc: &SamplerDesc) -> VkResult<Self> {
|
|
let info = &vk::SamplerCreateInfo::default()
|
|
.flags(desc.flags)
|
|
.min_filter(desc.min_filter)
|
|
.mag_filter(desc.mag_filter)
|
|
.mip_lod_bias(desc.mip_lod_bias)
|
|
.mipmap_mode(desc.mipmap_mode)
|
|
.address_mode_u(desc.address_u)
|
|
.address_mode_v(desc.address_v)
|
|
.address_mode_w(desc.address_w)
|
|
.anisotropy_enable(desc.anisotropy_enable)
|
|
.max_anisotropy(desc.max_anisotropy)
|
|
.compare_enable(desc.compare_op.is_some())
|
|
.compare_op(desc.compare_op.unwrap_or_default())
|
|
.min_lod(desc.min_lod)
|
|
.max_lod(desc.max_lod)
|
|
.border_color(desc.border_color)
|
|
.unnormalized_coordinates(desc.unnormalized_coordinates);
|
|
|
|
let handle = unsafe { device.dev().create_sampler(info, None)? };
|
|
|
|
Ok(Self {
|
|
sampler: DeviceObject::new(device, handle),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::ShaderModule {
|
|
unsafe fn destroy(self, device: &T) {
|
|
unsafe {
|
|
device.as_ref().raw.destroy_shader_module(self, None);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ShaderModule {
|
|
module: DeviceObject<vk::ShaderModule>,
|
|
}
|
|
|
|
impl ShaderModule {
|
|
pub fn raw(&self) -> vk::ShaderModule {
|
|
*self.module
|
|
}
|
|
pub fn new_from_path<P: AsRef<Path>>(device: Device, path: P) -> crate::Result<Self> {
|
|
use std::io::{BufReader, Read, Seek};
|
|
|
|
let path = path.as_ref();
|
|
let mut file =
|
|
std::fs::File::open(path).map_err(|_| crate::Error::AssetMissing(path.to_owned()))?;
|
|
let size = file.seek(std::io::SeekFrom::End(0))? / 4;
|
|
file.seek(std::io::SeekFrom::Start(0))?;
|
|
let mut reader = BufReader::new(file);
|
|
|
|
let mut buffer = vec![0; size as usize];
|
|
let size = reader.read(bytemuck::cast_slice_mut(buffer.as_mut_slice()))?;
|
|
buffer.resize(size / 4, 0);
|
|
|
|
Self::new_from_memory(device, &buffer)
|
|
}
|
|
|
|
pub fn new_from_memory(device: Device, buffer: &[u32]) -> crate::Result<Self> {
|
|
let info = &vk::ShaderModuleCreateInfo::default().code(buffer);
|
|
|
|
let module = unsafe { device.dev().create_shader_module(info, None)? };
|
|
|
|
Ok(Self {
|
|
module: DeviceObject::new(device, module),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Pipeline {
|
|
pipeline: DeviceObject<vk::Pipeline>,
|
|
bind_point: vk::PipelineBindPoint,
|
|
}
|
|
|
|
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Pipeline {
|
|
unsafe fn destroy(self, device: &T) {
|
|
unsafe {
|
|
device.as_ref().raw.destroy_pipeline(self, None);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ShaderStageDesc<'_> {
|
|
fn as_create_info(&'_ self) -> vk::PipelineShaderStageCreateInfo<'_> {
|
|
vk::PipelineShaderStageCreateInfo::default()
|
|
.module(self.module.raw())
|
|
.flags(self.flags)
|
|
.stage(self.stage)
|
|
.name(&self.entry)
|
|
}
|
|
}
|
|
|
|
impl Pipeline {
|
|
pub fn new_compute(device: Device, desc: ComputePipelineDesc) -> crate::Result<Self> {
|
|
let info = &vk::ComputePipelineCreateInfo::default()
|
|
.flags(desc.flags)
|
|
.layout(desc.layout.raw())
|
|
.base_pipeline_handle(
|
|
desc.base_pipeline
|
|
.map(|p| p.raw())
|
|
.unwrap_or(vk::Pipeline::null()),
|
|
)
|
|
.stage(desc.shader_stage.as_create_info());
|
|
|
|
let pipeline = unsafe {
|
|
device
|
|
.dev()
|
|
.create_compute_pipelines(
|
|
device.pools.pipeline_cache.raw,
|
|
core::slice::from_ref(info),
|
|
None,
|
|
)
|
|
// It's cool to just take the first one and ignore any
|
|
// potentially created pipelines since we know there wont be any
|
|
// others.
|
|
.map_err(|(_, err)| err)?[0]
|
|
};
|
|
|
|
Ok(Self {
|
|
pipeline: DeviceObject::new_debug_named(device, pipeline, desc.name),
|
|
bind_point: vk::PipelineBindPoint::COMPUTE,
|
|
})
|
|
}
|
|
|
|
pub fn new_graphics(device: Device, desc: GraphicsPipelineDesc) -> crate::Result<Self> {
|
|
let stages = desc
|
|
.shader_stages
|
|
.iter()
|
|
.map(|stage| stage.as_create_info())
|
|
.collect::<Vec<_>>();
|
|
|
|
let vertex_input = desc.vertex_input.map(|vertex| {
|
|
vk::PipelineVertexInputStateCreateInfo::default()
|
|
.vertex_attribute_descriptions(vertex.attributes)
|
|
.vertex_binding_descriptions(vertex.bindings)
|
|
});
|
|
let input_assembly = desc.input_assembly.map(|state| {
|
|
vk::PipelineInputAssemblyStateCreateInfo::default()
|
|
.primitive_restart_enable(state.primitive_restart)
|
|
.topology(state.topology)
|
|
});
|
|
let tessellation = desc.tessellation.map(|state| {
|
|
vk::PipelineTessellationStateCreateInfo::default()
|
|
.flags(state.flags)
|
|
.patch_control_points(state.patch_control_points)
|
|
});
|
|
let viewport = desc.viewport.map(|state| {
|
|
let mut info = vk::PipelineViewportStateCreateInfo::default()
|
|
.scissor_count(state.num_scissors)
|
|
.viewport_count(state.num_viewports);
|
|
if let Some(viewports) = state.viewports {
|
|
info = info.viewports(viewports);
|
|
}
|
|
if let Some(scissors) = state.scissors {
|
|
info = info.scissors(scissors);
|
|
}
|
|
|
|
info
|
|
});
|
|
|
|
let rasterization = desc.rasterization.map(|state| {
|
|
let mut info = vk::PipelineRasterizationStateCreateInfo::default()
|
|
.line_width(state.line_width)
|
|
.cull_mode(state.cull_mode)
|
|
.polygon_mode(state.polygon_mode)
|
|
.rasterizer_discard_enable(state.discard_enable)
|
|
.depth_clamp_enable(state.depth_clamp_enable);
|
|
|
|
if let Some(depth_bias) = state.depth_bias {
|
|
info = info
|
|
.depth_bias_enable(true)
|
|
.depth_bias_clamp(depth_bias.clamp)
|
|
.depth_bias_constant_factor(depth_bias.constant_factor)
|
|
.depth_bias_slope_factor(depth_bias.slope_factor);
|
|
}
|
|
|
|
info
|
|
});
|
|
|
|
let multisample = desc.multisample.map(|state| {
|
|
vk::PipelineMultisampleStateCreateInfo::default()
|
|
.flags(state.flags)
|
|
.min_sample_shading(state.min_sample_shading)
|
|
.rasterization_samples(state.rasterization_samples)
|
|
.sample_mask(state.sample_mask)
|
|
.sample_shading_enable(state.sample_shading_enable)
|
|
.alpha_to_coverage_enable(state.alpha_to_coverage_enable)
|
|
.alpha_to_one_enable(state.alpha_to_one_enable)
|
|
});
|
|
|
|
let color_blend = desc.color_blend.map(|state| {
|
|
vk::PipelineColorBlendStateCreateInfo::default()
|
|
.flags(state.flags)
|
|
.attachments(state.attachments)
|
|
.blend_constants(state.blend_constants)
|
|
.logic_op(state.logic_op.unwrap_or(Default::default()))
|
|
.logic_op_enable(state.logic_op.is_some())
|
|
});
|
|
|
|
let depth_stencil = desc.depth_stencil.map(|state| {
|
|
let mut info = vk::PipelineDepthStencilStateCreateInfo::default().flags(state.flags);
|
|
|
|
if let Some(depth) = state.depth {
|
|
info = info
|
|
.depth_compare_op(depth.compare_op.unwrap_or(vk::CompareOp::default()))
|
|
.depth_test_enable(depth.compare_op.is_some())
|
|
.depth_write_enable(depth.write_enable)
|
|
.depth_bounds_test_enable(depth.bounds.is_some());
|
|
if let Some(bounds) = depth.bounds {
|
|
info = info
|
|
.max_depth_bounds(bounds.max)
|
|
.min_depth_bounds(bounds.min);
|
|
}
|
|
}
|
|
|
|
if let Some(stencil) = state.stencil {
|
|
info = info
|
|
.stencil_test_enable(true)
|
|
.front(stencil.front)
|
|
.back(stencil.back);
|
|
}
|
|
|
|
info
|
|
});
|
|
|
|
let dynamic = desc.dynamic.map(|state| {
|
|
vk::PipelineDynamicStateCreateInfo::default()
|
|
.flags(state.flags)
|
|
.dynamic_states(state.dynamic_states)
|
|
});
|
|
|
|
let mut rendering = desc.rendering.map(|state| {
|
|
vk::PipelineRenderingCreateInfo::default()
|
|
.color_attachment_formats(state.color_formats)
|
|
.depth_attachment_format(state.depth_format.unwrap_or_default())
|
|
.stencil_attachment_format(state.stencil_format.unwrap_or_default())
|
|
});
|
|
|
|
fn option_to_ptr<T>(option: &Option<T>) -> *const T {
|
|
option
|
|
.as_ref()
|
|
.map(|t| t as *const T)
|
|
.unwrap_or(core::ptr::null())
|
|
}
|
|
|
|
let mut info = vk::GraphicsPipelineCreateInfo {
|
|
flags: desc.flags,
|
|
stage_count: stages.len() as u32,
|
|
p_stages: stages.as_ptr(),
|
|
p_vertex_input_state: option_to_ptr(&vertex_input),
|
|
p_input_assembly_state: option_to_ptr(&input_assembly),
|
|
p_tessellation_state: option_to_ptr(&tessellation),
|
|
p_viewport_state: option_to_ptr(&viewport),
|
|
p_rasterization_state: option_to_ptr(&rasterization),
|
|
p_multisample_state: option_to_ptr(&multisample),
|
|
p_depth_stencil_state: option_to_ptr(&depth_stencil),
|
|
p_color_blend_state: option_to_ptr(&color_blend),
|
|
p_dynamic_state: option_to_ptr(&dynamic),
|
|
layout: desc.layout.raw(),
|
|
render_pass: desc.render_pass.unwrap_or(vk::RenderPass::null()),
|
|
subpass: desc.subpass.unwrap_or(0),
|
|
base_pipeline_handle: desc
|
|
.base_pipeline
|
|
.map(|piepline| *piepline.pipeline)
|
|
.unwrap_or(vk::Pipeline::null()),
|
|
..Default::default()
|
|
};
|
|
|
|
if let Some(rendering) = rendering.as_mut() {
|
|
info = info.push_next(rendering)
|
|
}
|
|
|
|
let pipeline = unsafe {
|
|
device
|
|
.dev()
|
|
.create_graphics_pipelines(
|
|
device.pools.pipeline_cache.raw,
|
|
core::slice::from_ref(&info),
|
|
None,
|
|
)
|
|
// It's cool to just take the first one and ignore any
|
|
// potentially created pipelines since we know there wont be any
|
|
// others.
|
|
.map_err(|(_, err)| err)?[0]
|
|
};
|
|
|
|
Ok(Self {
|
|
pipeline: DeviceObject::new_debug_named(device, pipeline, desc.name),
|
|
bind_point: vk::PipelineBindPoint::GRAPHICS,
|
|
})
|
|
}
|
|
|
|
pub fn raw(&self) -> vk::Pipeline {
|
|
*self.pipeline
|
|
}
|
|
pub fn bind_point(&self) -> vk::PipelineBindPoint {
|
|
self.bind_point
|
|
}
|
|
}
|
|
|
|
pub(crate) mod pipeline_cache {
|
|
use std::sync::Arc;
|
|
|
|
use ash::vk;
|
|
|
|
use ash::Device;
|
|
|
|
use crate::PhysicalDeviceInfo;
|
|
use crate::device::DeviceInner;
|
|
|
|
#[derive(Debug)]
|
|
pub struct PipelineCache {
|
|
#[allow(dead_code)]
|
|
key: u128,
|
|
pub(crate) raw: vk::PipelineCache,
|
|
}
|
|
|
|
impl crate::device::asdf::traits::ExternallyManagedObject<Arc<DeviceInner>> for PipelineCache {
|
|
unsafe fn destroy(self, owner: &Arc<DeviceInner>) {
|
|
tracing::info!("destroying pipeline cache with key {:x}", self.key);
|
|
if let Ok(data) = self.export(&owner.raw) {
|
|
tracing::info!(
|
|
"exported pipeline cache with key {:x} and size {} bytes",
|
|
self.key,
|
|
data.len()
|
|
);
|
|
_ = Self::write_to_disk(self.key, &data).inspect_err(|err| {
|
|
tracing::error!("failed to write pipeline cache to disk: {err}");
|
|
});
|
|
}
|
|
unsafe { owner.raw.destroy_pipeline_cache(self.raw, None) };
|
|
}
|
|
}
|
|
|
|
impl PipelineCache {
|
|
const MAGIC: [u8; 4] = *b"VYPC";
|
|
const KEY_VERSION: u32 = 1;
|
|
const PATH: &'static str = "pipeline_cache.bin";
|
|
fn calculate_key(adapter: &PhysicalDeviceInfo) -> u128 {
|
|
use md5::Digest;
|
|
let mut hasher = md5::Md5::new();
|
|
let props = &adapter.properties;
|
|
hasher.update(bytemuck::bytes_of(&[
|
|
props.core.vendor_id,
|
|
props.core.api_version,
|
|
props.core.device_id,
|
|
props.core.driver_version,
|
|
]));
|
|
u128::from_le_bytes(hasher.finalize().into())
|
|
}
|
|
|
|
fn load_from_disk(key: u128) -> Option<(u128, Vec<u8>)> {
|
|
use std::io::Read;
|
|
|
|
let file = std::fs::File::open(Self::PATH).ok()?;
|
|
let mut reader = std::io::BufReader::new(file);
|
|
let mut magic = [0; 4];
|
|
reader.read_exact(&mut magic).ok()?;
|
|
if magic != Self::MAGIC {
|
|
return None;
|
|
}
|
|
|
|
let mut version = 0;
|
|
reader
|
|
.read_exact(bytemuck::bytes_of_mut(&mut version))
|
|
.ok()?;
|
|
if version != Self::KEY_VERSION {
|
|
return None;
|
|
}
|
|
|
|
let mut disk_key = 0;
|
|
reader
|
|
.read_exact(bytemuck::bytes_of_mut(&mut disk_key))
|
|
.ok()?;
|
|
if disk_key != key {
|
|
return None;
|
|
}
|
|
|
|
let mut data = Vec::new();
|
|
reader.read_to_end(&mut data).ok()?;
|
|
|
|
Some((key, data))
|
|
}
|
|
|
|
fn write_to_disk(key: u128, data: &[u8]) -> std::io::Result<()> {
|
|
use std::io::Write;
|
|
|
|
let file = std::fs::File::create(Self::PATH)?;
|
|
let mut writer = std::io::BufWriter::new(file);
|
|
writer.write_all(&Self::MAGIC)?;
|
|
writer.write_all(bytemuck::bytes_of(&Self::KEY_VERSION))?;
|
|
writer.write_all(bytemuck::bytes_of(&key))?;
|
|
writer.write_all(data)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn new(device: &Device, adapter: &PhysicalDeviceInfo) -> crate::Result<Self> {
|
|
let key = Self::calculate_key(adapter);
|
|
let data = Self::load_from_disk(key).map(|(key, data)| {
|
|
tracing::info!("loaded pipeline cache from disk with key {key:x}");
|
|
data
|
|
});
|
|
|
|
let info = vk::PipelineCacheCreateInfo::default()
|
|
// .flags(vk::PipelineCacheCreateFlags::EXTERNALLY_SYNCHRONIZED)
|
|
.initial_data(data.as_deref().unwrap_or_default());
|
|
|
|
let cache = unsafe { device.create_pipeline_cache(&info, None)? };
|
|
|
|
Ok(Self { key, raw: cache })
|
|
}
|
|
|
|
pub fn export(&self, device: &ash::Device) -> crate::Result<Vec<u8>> {
|
|
let data = unsafe { device.get_pipeline_cache_data(self.raw)? };
|
|
Ok(data)
|
|
}
|
|
}
|
|
}
|