vulkan: compile shader modules at pipeline creation time

This commit is contained in:
Dzmitry Malyshau 2021-09-07 19:35:19 -04:00 committed by Dzmitry Malyshau
parent ce35395910
commit e00bfac6cf
6 changed files with 147 additions and 60 deletions

View File

@ -36,7 +36,7 @@ thiserror = "1"
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "93db57c"
rev = "d4bedaf"
#version = "0.6"
features = ["wgsl-in"]

View File

@ -69,12 +69,12 @@ core-graphics-types = "0.1"
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "93db57c"
rev = "d4bedaf"
#version = "0.6"
[dev-dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "93db57c"
rev = "d4bedaf"
#version = "0.6"
features = ["wgsl-in"]

View File

@ -53,13 +53,11 @@ impl PhysicalDeviceFeatures {
downlevel_flags: wgt::DownlevelFlags,
private_caps: &super::PrivateCapabilities,
) -> Self {
//TODO: make configurable
let rba = !(cfg!(target_os = "macos") || cfg!(target_os = "ios"));
Self {
// vk::PhysicalDeviceFeatures is a struct composed of Bool32's while
// Features is a bitfield so we need to map everything manually
core: vk::PhysicalDeviceFeatures::builder()
.robust_buffer_access(rba)
.robust_buffer_access(private_caps.robust_buffer_access)
.independent_blend(true)
.sample_rate_shading(true)
.image_cube_array(
@ -631,6 +629,7 @@ impl super::Instance {
};
let (available_features, downlevel_flags) = phd_features.to_wgpu(&phd_capabilities);
let mut workarounds = super::Workarounds::empty();
{
use crate::auxil::db;
// see https://github.com/gfx-rs/gfx/issues/1930
@ -640,6 +639,8 @@ impl super::Instance {
== db::intel::DEVICE_KABY_LAKE_MASK
|| phd_capabilities.properties.device_id & db::intel::DEVICE_SKY_LAKE_MASK
== db::intel::DEVICE_SKY_LAKE_MASK);
// TODO: only enable for particular devices
workarounds |= super::Workarounds::SEPARATE_ENTRY_POINTS;
};
if phd_features.core.sample_rate_shading == 0 {
@ -700,6 +701,8 @@ impl super::Instance {
},
non_coherent_map_mask: phd_capabilities.properties.limits.non_coherent_atom_size - 1,
can_present: true,
//TODO: make configurable
robust_buffer_access: phd_features.core.robust_buffer_access != 0,
};
let capabilities = crate::Capabilities {
@ -725,6 +728,7 @@ impl super::Instance {
//phd_features,
downlevel_flags,
private_caps,
workarounds,
};
Some(crate::ExposedAdapter {
@ -845,8 +849,15 @@ impl super::Adapter {
lang_version: (1, 0),
flags,
capabilities: Some(capabilities.iter().cloned().collect()),
index_bounds_check_policy: naga::back::BoundsCheckPolicy::Restrict,
image_bounds_check_policy: naga::back::BoundsCheckPolicy::Restrict,
bounds_check_policies: naga::back::BoundsCheckPolicies {
index: naga::back::BoundsCheckPolicy::Restrict,
image: naga::back::BoundsCheckPolicy::Restrict,
buffer: if self.private_caps.robust_buffer_access {
naga::back::BoundsCheckPolicy::Unchecked
} else {
naga::back::BoundsCheckPolicy::Restrict
},
},
}
};
@ -864,6 +875,7 @@ impl super::Adapter {
vendor_id: self.phd_capabilities.properties.vendor_id,
downlevel_flags: self.downlevel_flags,
private_caps: self.private_caps.clone(),
workarounds: self.workarounds,
timestamp_period: self.phd_capabilities.properties.limits.timestamp_period,
render_passes: Mutex::new(Default::default()),
framebuffers: Mutex::new(Default::default()),

View File

@ -460,6 +460,12 @@ impl
}
}
struct CompiledStage {
create_info: vk::PipelineShaderStageCreateInfo,
_entry_point: CString,
temp_raw_module: Option<vk::ShaderModule>,
}
impl super::Device {
pub(super) unsafe fn create_swapchain(
&self,
@ -555,6 +561,59 @@ impl super::Device {
copy_size: conv::map_extent_to_copy_size(&desc.size, desc.dimension),
}
}
fn create_shader_module_impl(
&self,
spv: &[u32],
) -> Result<vk::ShaderModule, crate::DeviceError> {
let vk_info = vk::ShaderModuleCreateInfo::builder()
.flags(vk::ShaderModuleCreateFlags::empty())
.code(spv);
let raw = unsafe { self.shared.raw.create_shader_module(&vk_info, None)? };
Ok(raw)
}
fn compile_stage(
&self,
stage: &crate::ProgrammableStage<super::Api>,
naga_stage: naga::ShaderStage,
) -> Result<CompiledStage, crate::PipelineError> {
let stage_flags = crate::auxil::map_naga_stage(naga_stage);
let vk_module = match *stage.module {
super::ShaderModule::Raw(raw) => raw,
super::ShaderModule::Intermediate(ref naga_shader) => {
let pipeline_options = naga::back::spv::PipelineOptions {
entry_point: stage.entry_point.to_string(),
shader_stage: naga_stage,
};
let spv = naga::back::spv::write_vec(
&naga_shader.module,
&naga_shader.info,
&self.naga_options,
Some(&pipeline_options),
)
.map_err(|e| crate::PipelineError::Linkage(stage_flags, format!("{}", e)))?;
self.create_shader_module_impl(&spv)?
}
};
let entry_point = CString::new(stage.entry_point).unwrap();
let create_info = vk::PipelineShaderStageCreateInfo::builder()
.stage(conv::map_shader_stage(stage_flags))
.module(vk_module)
.name(&entry_point)
.build();
Ok(CompiledStage {
create_info,
_entry_point: entry_point,
temp_raw_module: match *stage.module {
super::ShaderModule::Raw(_) => None,
super::ShaderModule::Intermediate(_) => Some(vk_module),
},
})
}
}
impl crate::Device<super::Api> for super::Device {
@ -1114,36 +1173,43 @@ impl crate::Device<super::Api> for super::Device {
shader: crate::ShaderInput,
) -> Result<super::ShaderModule, crate::ShaderError> {
let spv = match shader {
crate::ShaderInput::Naga(naga_shader) => Cow::Owned(
crate::ShaderInput::Naga(naga_shader) => {
if self
.shared
.workarounds
.contains(super::Workarounds::SEPARATE_ENTRY_POINTS)
{
return Ok(super::ShaderModule::Intermediate(naga_shader));
}
Cow::Owned(
naga::back::spv::write_vec(
&naga_shader.module,
&naga_shader.info,
&self.naga_options,
None,
)
.map_err(|e| crate::ShaderError::Compilation(format!("{}", e)))?,
),
)
}
crate::ShaderInput::SpirV(spv) => Cow::Borrowed(spv),
};
let vk_info = vk::ShaderModuleCreateInfo::builder()
.flags(vk::ShaderModuleCreateFlags::empty())
.code(&spv);
let raw = self
.shared
.raw
.create_shader_module(&vk_info, None)
.map_err(crate::DeviceError::from)?;
let raw = self.create_shader_module_impl(&*spv)?;
if let Some(label) = desc.label {
self.shared
.set_object_name(vk::ObjectType::SHADER_MODULE, raw, label);
}
Ok(super::ShaderModule { raw })
Ok(super::ShaderModule::Raw(raw))
}
unsafe fn destroy_shader_module(&self, module: super::ShaderModule) {
let _ = self.shared.raw.destroy_shader_module(module.raw, None);
match module {
super::ShaderModule::Raw(raw) => {
let _ = self.shared.raw.destroy_shader_module(raw, None);
}
super::ShaderModule::Intermediate(_) => {}
}
}
unsafe fn create_render_pipeline(
@ -1193,29 +1259,16 @@ impl crate::Device<super::Api> for super::Device {
.primitive_restart_enable(desc.primitive.strip_index_format.is_some())
.build();
let vs_entry_point;
{
let stage = &desc.vertex_stage;
vs_entry_point = CString::new(stage.entry_point).unwrap();
stages.push(
vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::VERTEX)
.module(stage.module.raw)
.name(&vs_entry_point)
.build(),
);
}
let fs_entry_point;
if let Some(ref stage) = desc.fragment_stage {
fs_entry_point = CString::new(stage.entry_point).unwrap();
stages.push(
vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(stage.module.raw)
.name(&fs_entry_point)
.build(),
);
let compiled_vs = self.compile_stage(&desc.vertex_stage, naga::ShaderStage::Vertex)?;
stages.push(compiled_vs.create_info);
let compiled_fs = match desc.fragment_stage {
Some(ref stage) => {
let compiled = self.compile_stage(stage, naga::ShaderStage::Fragment)?;
stages.push(compiled.create_info);
Some(compiled)
}
None => None,
};
let mut vk_rasterization = vk::PipelineRasterizationStateCreateInfo::builder()
.depth_clamp_enable(desc.primitive.clamp_depth)
@ -1355,6 +1408,17 @@ impl crate::Device<super::Api> for super::Device {
.set_object_name(vk::ObjectType::PIPELINE, raw, label);
}
if let Some(raw_module) = compiled_vs.temp_raw_module {
self.shared.raw.destroy_shader_module(raw_module, None);
}
if let Some(CompiledStage {
temp_raw_module: Some(raw_module),
..
}) = compiled_fs
{
self.shared.raw.destroy_shader_module(raw_module, None);
}
Ok(super::RenderPipeline { raw })
}
unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) {
@ -1365,17 +1429,12 @@ impl crate::Device<super::Api> for super::Device {
&self,
desc: &crate::ComputePipelineDescriptor<super::Api>,
) -> Result<super::ComputePipeline, crate::PipelineError> {
let cs_entry_point = CString::new(desc.stage.entry_point).unwrap();
let vk_stage = vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::COMPUTE)
.module(desc.stage.module.raw)
.name(&cs_entry_point)
.build();
let compiled = self.compile_stage(&desc.stage, naga::ShaderStage::Compute)?;
let vk_infos = [{
vk::ComputePipelineCreateInfo::builder()
.layout(desc.layout.raw)
.stage(vk_stage)
.stage(compiled.create_info)
.build()
}];
@ -1391,6 +1450,10 @@ impl crate::Device<super::Api> for super::Device {
.set_object_name(vk::ObjectType::PIPELINE, raw, label);
}
if let Some(raw_module) = compiled.temp_raw_module {
self.shared.raw.destroy_shader_module(raw_module, None);
}
Ok(super::ComputePipeline { raw })
}
unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) {

View File

@ -130,6 +130,7 @@ pub struct Adapter {
//phd_features: adapter::PhysicalDeviceFeatures,
downlevel_flags: wgt::DownlevelFlags,
private_caps: PrivateCapabilities,
workarounds: Workarounds,
}
// TODO there's no reason why this can't be unified--the function pointers should all be the same--it's not clear how to do this with `ash`.
@ -161,8 +162,17 @@ struct PrivateCapabilities {
/// Ability to present contents to any screen. Only needed to work around broken platform configurations.
can_present: bool,
non_coherent_map_mask: wgt::BufferAddress,
robust_buffer_access: bool,
}
bitflags::bitflags!(
/// Workaround flags.
pub struct Workarounds: u32 {
/// Only generate SPIR-V for one entry point at a time.
const SEPARATE_ENTRY_POINTS = 0x1;
}
);
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct AttachmentKey {
format: vk::Format,
@ -225,6 +235,7 @@ struct DeviceShared {
timestamp_period: f32,
downlevel_flags: wgt::DownlevelFlags,
private_caps: PrivateCapabilities,
workarounds: Workarounds,
render_passes: Mutex<fxhash::FxHashMap<RenderPassKey, vk::RenderPass>>,
framebuffers: Mutex<fxhash::FxHashMap<FramebufferKey, vk::Framebuffer>>,
}
@ -358,8 +369,9 @@ pub struct CommandBuffer {
}
#[derive(Debug)]
pub struct ShaderModule {
raw: vk::ShaderModule,
pub enum ShaderModule {
Raw(vk::ShaderModule),
Intermediate(crate::NagaShader),
}
#[derive(Debug)]

View File

@ -90,14 +90,14 @@ env_logger = "0.8"
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "93db57c"
rev = "d4bedaf"
#version = "0.6"
optional = true
# used to test all the example shaders
[dev-dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "93db57c"
rev = "d4bedaf"
#version = "0.6"
features = ["wgsl-in"]