Add API to enable ext/capabilities, and remove default capabilities (#630)

* Do not set some capabilities by default

* Fix nondeterminism

* Add required caps to mouse shader
This commit is contained in:
Ashley Hauck 2021-05-26 11:57:16 +02:00 committed by GitHub
parent 255a6f09e6
commit 6019f391ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 195 additions and 160 deletions

View File

@ -15,6 +15,7 @@ use rustc_middle::ty::{
}; };
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::Span; use rustc_span::Span;
use rustc_span::DUMMY_SP;
use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind}; use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
use rustc_target::abi::{ use rustc_target::abi::{
Abi, Align, FieldsShape, LayoutOf, Primitive, Scalar, Size, TagEncoding, VariantIdx, Variants, Abi, Align, FieldsShape, LayoutOf, Primitive, Scalar, Size, TagEncoding, VariantIdx, Variants,
@ -323,11 +324,15 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
fn trans_type_impl<'tcx>( fn trans_type_impl<'tcx>(
cx: &CodegenCx<'tcx>, cx: &CodegenCx<'tcx>,
span: Span, mut span: Span,
ty: TyAndLayout<'tcx>, ty: TyAndLayout<'tcx>,
is_immediate: bool, is_immediate: bool,
) -> Word { ) -> Word {
if let TyKind::Adt(adt, substs) = *ty.ty.kind() { if let TyKind::Adt(adt, substs) = *ty.ty.kind() {
if span == DUMMY_SP {
span = cx.tcx.def_span(adt.did);
}
let attrs = AggregatedSpirvAttributes::parse(cx, cx.tcx.get_attrs(adt.did)); let attrs = AggregatedSpirvAttributes::parse(cx, cx.tcx.get_attrs(adt.did));
if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) { if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) {

View File

@ -1,6 +1,5 @@
use super::Builder; use super::Builder;
use crate::builder_spirv::{SpirvValue, SpirvValueExt}; use crate::builder_spirv::{SpirvValue, SpirvValueExt};
use crate::codegen_cx::CodegenCx;
use rspirv::spirv::{CLOp, GLOp, Word}; use rspirv::spirv::{CLOp, GLOp, Word};
use rspirv::{dr::Operand, spirv::Capability}; use rspirv::{dr::Operand, spirv::Capability};
@ -40,14 +39,26 @@ impl ExtInst {
} }
} }
pub fn import_integer_functions_2_intel<'tcx>(&mut self, cx: &CodegenCx<'tcx>) { pub fn require_integer_functions_2_intel<'a, 'tcx>(
&mut self,
bx: &Builder<'a, 'tcx>,
to_zombie: Word,
) {
if !self.integer_functions_2_intel { if !self.integer_functions_2_intel {
assert!(!cx.target.is_kernel()); assert!(!bx.target.is_kernel());
self.integer_functions_2_intel = true; self.integer_functions_2_intel = true;
cx.emit_global() if !bx
.extension("SPV_INTEL_shader_integer_functions2"); .builder
cx.emit_global() .has_capability(Capability::IntegerFunctions2INTEL)
.capability(Capability::IntegerFunctions2INTEL); {
bx.zombie(to_zombie, "capability IntegerFunctions2INTEL is required");
}
if !bx
.builder
.has_extension("SPV_INTEL_shader_integer_functions2")
{
bx.zombie(to_zombie, "extension IntegerFunctions2INTEL is required");
}
} }
} }
} }

View File

@ -362,34 +362,36 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
if self.target.is_kernel() { if self.target.is_kernel() {
self.cl_op(CLOp::clz, ret_ty, [args[0].immediate()]) self.cl_op(CLOp::clz, ret_ty, [args[0].immediate()])
} else { } else {
self.ext_inst let result = self
.borrow_mut() .emit()
.import_integer_functions_2_intel(self);
self.emit()
.u_count_leading_zeros_intel( .u_count_leading_zeros_intel(
args[0].immediate().ty, args[0].immediate().ty,
None, None,
args[0].immediate().def(self), args[0].immediate().def(self),
) )
.unwrap() .unwrap();
.with_type(args[0].immediate().ty) self.ext_inst
.borrow_mut()
.require_integer_functions_2_intel(self, result);
result.with_type(args[0].immediate().ty)
} }
} }
sym::cttz | sym::cttz_nonzero => { sym::cttz | sym::cttz_nonzero => {
if self.target.is_kernel() { if self.target.is_kernel() {
self.cl_op(CLOp::ctz, ret_ty, [args[0].immediate()]) self.cl_op(CLOp::ctz, ret_ty, [args[0].immediate()])
} else { } else {
self.ext_inst let result = self
.borrow_mut() .emit()
.import_integer_functions_2_intel(self);
self.emit()
.u_count_trailing_zeros_intel( .u_count_trailing_zeros_intel(
args[0].immediate().ty, args[0].immediate().ty,
None, None,
args[0].immediate().def(self), args[0].immediate().def(self),
) )
.unwrap() .unwrap();
.with_type(args[0].immediate().ty) self.ext_inst
.borrow_mut()
.require_integer_functions_2_intel(self, result);
result.with_type(args[0].immediate().ty)
} }
} }

View File

@ -350,10 +350,6 @@ impl BuilderSpirv {
// The linker will always be ran on this module // The linker will always be ran on this module
builder.capability(Capability::Linkage); builder.capability(Capability::Linkage);
builder.capability(Capability::Int8);
builder.capability(Capability::Int16);
builder.capability(Capability::Int64);
builder.capability(Capability::Float64);
let addressing_model = if target.is_kernel() { let addressing_model = if target.is_kernel() {
builder.capability(Capability::Addresses); builder.capability(Capability::Addresses);
@ -425,6 +421,18 @@ impl BuilderSpirv {
}) })
} }
pub fn has_extension(&self, extension: &str) -> bool {
self.builder
.borrow()
.module_ref()
.extensions
.iter()
.any(|inst| {
inst.class.opcode == Op::Extension
&& inst.operands[0].unwrap_literal_string() == extension
})
}
pub fn select_function_by_id(&self, id: Word) -> BuilderCursor { pub fn select_function_by_id(&self, id: Word) -> BuilderCursor {
let mut builder = self.builder.borrow_mut(); let mut builder = self.builder.borrow_mut();
for (index, func) in builder.module_ref().functions.iter().enumerate() { for (index, func) in builder.module_ref().functions.iter().enumerate() {

View File

@ -96,12 +96,22 @@ impl<'tcx> CodegenCx<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>) -> Self { pub fn new(tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>) -> Self {
let sym = Symbols::get(); let sym = Symbols::get();
let features = tcx let mut feature_names = tcx
.sess .sess
.target_features .target_features
.iter() .iter()
.filter(|s| *s != &sym.bindless) .filter(|s| *s != &sym.bindless)
.map(|s| s.as_str().parse()) .map(|s| s.as_str())
.collect::<Vec<_>>();
// target_features is a HashSet, not a Vec, so we need to sort to have deterministic
// compilation - otherwise, the order of capabilities in binaries depends on the iteration
// order of the hashset. Sort by the string, since that's easy.
feature_names.sort();
let features = feature_names
.into_iter()
.map(|s| s.parse())
.collect::<Result<_, String>>() .collect::<Result<_, String>>()
.unwrap_or_else(|error| { .unwrap_or_else(|error| {
tcx.sess.err(&error); tcx.sess.err(&error);
@ -221,6 +231,7 @@ impl<'tcx> CodegenCx<'tcx> {
|| self.tcx.crate_name(LOCAL_CRATE) == self.sym.spirv_std || self.tcx.crate_name(LOCAL_CRATE) == self.sym.spirv_std
|| self.tcx.crate_name(LOCAL_CRATE) == self.sym.libm || self.tcx.crate_name(LOCAL_CRATE) == self.sym.libm
|| self.tcx.crate_name(LOCAL_CRATE) == self.sym.num_traits || self.tcx.crate_name(LOCAL_CRATE) == self.sym.num_traits
|| self.tcx.crate_name(LOCAL_CRATE) == self.sym.glam
} }
// FIXME(eddyb) should this just be looking at `kernel_mode`? // FIXME(eddyb) should this just be looking at `kernel_mode`?

View File

@ -1,105 +0,0 @@
use rspirv::dr::{Instruction, Module};
use rspirv::spirv::{Capability, Op};
use rustc_data_structures::fx::FxHashSet;
pub fn remove_extra_capabilities(module: &mut Module) {
let used_capabilities = used_capabilities(module);
let removable_capabilities: FxHashSet<Capability> = [
Capability::Int8,
Capability::Int16,
Capability::Int64,
Capability::Float16,
Capability::Float64,
Capability::IntegerFunctions2INTEL,
Capability::DemoteToHelperInvocationEXT,
Capability::DerivativeControl,
]
.iter()
.copied()
.collect();
let to_remove = removable_capabilities
.difference(&used_capabilities)
.copied()
.collect();
remove_capabilities(module, &to_remove);
}
fn used_capabilities(module: &Module) -> FxHashSet<Capability> {
let mut set = FxHashSet::default();
for inst in module.all_inst_iter() {
set.extend(inst.class.capabilities);
match inst.class.opcode {
Op::TypeInt => match inst.operands[0].unwrap_literal_int32() {
8 => {
set.insert(Capability::Int8);
}
16 => {
set.insert(Capability::Int16);
}
64 => {
set.insert(Capability::Int64);
}
_ => {}
},
Op::TypeFloat => match inst.operands[0].unwrap_literal_int32() {
16 => {
set.insert(Capability::Float16);
}
64 => {
set.insert(Capability::Float64);
}
_ => {}
},
_ => {}
}
}
set
}
fn remove_capabilities(module: &mut Module, set: &FxHashSet<Capability>) {
module.capabilities.retain(|inst| {
inst.class.opcode != Op::Capability || !set.contains(&inst.operands[0].unwrap_capability())
});
}
// rspirv pulls its spec information from the latest version. However, we might not be compiling for
// the latest version.
// For example, we might run into this situation:
// OpCapability VulkanMemoryModel in SPIR-V v1.5 requires no extensions
// OpCapability VulkanMemoryModel in SPIR-V <= v1.4 requires OpExtension SPV_KHR_vulkan_memory_model
// rspirv uses SPIR-V v1.5 (as of now), and so it states that VulkanMemoryModel needs no extensions
// We're compiling for, say, SPIR-V 1.3, and ask rspirv if VulkanMemoryModel requires an extension
// It says no. We strip it. Things explode.
// So, this function is to encode any special version-specific rules that aren't in rspirv.
fn additional_extensions(module: &Module, inst: &Instruction) -> &'static [&'static str] {
if inst.class.opcode == Op::Capability {
let version = module.header.as_ref().unwrap().version();
match inst.operands[0].unwrap_capability() {
Capability::VulkanMemoryModel if version < (1, 5) => &["SPV_KHR_vulkan_memory_model"],
Capability::RuntimeDescriptorArray if version < (1, 5) => {
&["SPV_EXT_descriptor_indexing"]
}
_ => &[],
}
} else {
&[]
}
}
pub fn remove_extra_extensions(module: &mut Module) {
let set: FxHashSet<&str> = module
.all_inst_iter()
.flat_map(|inst| {
let extensions = inst.class.extensions.iter().copied();
let operand_extensions = inst.operands.iter().flat_map(|op| op.required_extensions());
let additional_extensions = additional_extensions(module, inst).iter().copied();
extensions
.chain(operand_extensions)
.chain(additional_extensions)
})
.collect();
module.extensions.retain(|inst| {
inst.class.opcode != Op::Extension || set.contains(inst.operands[0].unwrap_literal_string())
})
}

View File

@ -56,7 +56,7 @@ fn find_import_export_pairs_and_killed_params(
}; };
let import_type = *type_map.get(&import_id).expect("Unexpected op"); let import_type = *type_map.get(&import_id).expect("Unexpected op");
// Make sure the import/export pair has the same type. // Make sure the import/export pair has the same type.
check_tys_equal(sess, name, import_type, export_type)?; check_tys_equal(sess, module, name, import_type, export_type)?;
rewrite_rules.insert(import_id, export_id); rewrite_rules.insert(import_id, export_id);
if let Some(params) = fn_parameters.get(&import_id) { if let Some(params) = fn_parameters.get(&import_id) {
for &param in params { for &param in params {
@ -108,11 +108,56 @@ fn fn_parameters(module: &Module) -> FxHashMap<Word, Vec<Word>> {
.collect() .collect()
} }
fn check_tys_equal(sess: &Session, name: &str, import_type: Word, export_type: Word) -> Result<()> { fn check_tys_equal(
sess: &Session,
module: &Module,
name: &str,
import_type: Word,
export_type: Word,
) -> Result<()> {
if import_type == export_type { if import_type == export_type {
Ok(()) Ok(())
} else { } else {
sess.err(&format!("Types mismatch for {:?}", name)); // We have an error. It's okay to do something really slow now to report the error.
use std::fmt::Write;
let ty_defs = module
.types_global_values
.iter()
.filter_map(|inst| Some((inst.result_id?, inst)))
.collect();
fn format_ty(ty_defs: &FxHashMap<Word, &Instruction>, ty: Word, buf: &mut String) {
match ty_defs.get(&ty) {
Some(def) => {
write!(buf, "({}", def.class.opname).unwrap();
if let Some(result_type) = def.result_type {
write!(buf, " {}", result_type).unwrap();
}
for op in &def.operands {
if let Some(id) = op.id_ref_any() {
write!(buf, " ").unwrap();
format_ty(ty_defs, id, buf);
}
}
write!(buf, ")").unwrap();
}
None => write!(buf, "{}", ty).unwrap(),
}
}
fn format_ty_(ty_defs: &FxHashMap<Word, &Instruction>, ty: Word) -> String {
let mut result = String::new();
format_ty(ty_defs, ty, &mut result);
result
}
sess.struct_err(&format!("Types mismatch for {:?}", name))
.note(&format!(
"import type: {}",
format_ty_(&ty_defs, import_type)
))
.note(&format!(
"export type: {}",
format_ty_(&ty_defs, export_type)
))
.emit();
Err(ErrorReported) Err(ErrorReported)
} }
} }

View File

@ -1,7 +1,6 @@
#[cfg(test)] #[cfg(test)]
mod test; mod test;
mod capability_computation;
mod dce; mod dce;
mod duplicates; mod duplicates;
mod import_export_link; mod import_export_link;
@ -292,11 +291,6 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
let _timer = sess.timer("link_dce_2"); let _timer = sess.timer("link_dce_2");
dce::dce(output); dce::dce(output);
} }
{
let _timer = sess.timer("link_remove_extra_capabilities");
capability_computation::remove_extra_capabilities(output);
capability_computation::remove_extra_extensions(output);
}
if opts.compact_ids { if opts.compact_ids {
let _timer = sess.timer("link_compact_ids"); let _timer = sess.timer("link_compact_ids");

View File

@ -224,7 +224,7 @@ fn type_mismatch() {
let result = assemble_and_link(&[&a, &b]); let result = assemble_and_link(&[&a, &b]);
assert_eq!( assert_eq!(
result.err().as_deref(), result.err().as_deref(),
Some("error: Types mismatch for \"foo\"") Some("error: Types mismatch for \"foo\"\n |\n = note: import type: (TypeFloat)\n = note: export type: (TypeInt)")
); );
} }

View File

@ -20,6 +20,7 @@ pub struct Symbols {
pub spirv_std: Symbol, pub spirv_std: Symbol,
pub libm: Symbol, pub libm: Symbol,
pub num_traits: Symbol, pub num_traits: Symbol,
pub glam: Symbol,
pub entry_point_name: Symbol, pub entry_point_name: Symbol,
descriptor_set: Symbol, descriptor_set: Symbol,
binding: Symbol, binding: Symbol,
@ -374,6 +375,7 @@ impl Symbols {
spirv_std: Symbol::intern("spirv_std"), spirv_std: Symbol::intern("spirv_std"),
libm: Symbol::intern("libm"), libm: Symbol::intern("libm"),
num_traits: Symbol::intern("num_traits"), num_traits: Symbol::intern("num_traits"),
glam: Symbol::intern("glam"),
descriptor_set: Symbol::intern("descriptor_set"), descriptor_set: Symbol::intern("descriptor_set"),
binding: Symbol::intern("binding"), binding: Symbol::intern("binding"),
image_type: Symbol::intern("image_type"), image_type: Symbol::intern("image_type"),

View File

@ -66,6 +66,7 @@ use std::io::BufReader;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
pub use rustc_codegen_spirv::rspirv::spirv::Capability;
pub use rustc_codegen_spirv::{CompileResult, ModuleResult}; pub use rustc_codegen_spirv::{CompileResult, ModuleResult};
#[derive(Debug)] #[derive(Debug)]
@ -112,6 +113,8 @@ pub struct SpirvBuilder {
target: String, target: String,
bindless: bool, bindless: bool,
multimodule: bool, multimodule: bool,
capabilities: Vec<Capability>,
extensions: Vec<String>,
} }
impl SpirvBuilder { impl SpirvBuilder {
@ -123,6 +126,8 @@ impl SpirvBuilder {
target: target.into(), target: target.into(),
bindless: false, bindless: false,
multimodule: false, multimodule: false,
capabilities: Vec::new(),
extensions: Vec::new(),
} }
} }
@ -153,6 +158,20 @@ impl SpirvBuilder {
self self
} }
/// Adds a capability to the SPIR-V module. Checking if a capability is enabled in code can be
/// done via `#[cfg(target_feature = "TheCapability")]`.
pub fn capability(mut self, capability: Capability) -> Self {
self.capabilities.push(capability);
self
}
/// Adds an extension to the SPIR-V module. Checking if an extension is enabled in code can be
/// done via `#[cfg(target_feature = "ext:the_extension")]`.
pub fn extension(mut self, extension: impl Into<String>) -> Self {
self.extensions.push(extension.into());
self
}
/// Builds the module. If `print_metadata` is true, you usually don't have to inspect the path /// Builds the module. If `print_metadata` is true, you usually don't have to inspect the path
/// in the result, as the environment variable for the path to the module will already be set. /// in the result, as the environment variable for the path to the module will already be set.
pub fn build(self) -> Result<CompileResult, SpirvBuilderError> { pub fn build(self) -> Result<CompileResult, SpirvBuilderError> {
@ -234,8 +253,10 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
let mut target_features = Vec::new(); let mut target_features = Vec::new();
if builder.bindless { if builder.bindless {
target_features.push("+bindless"); target_features.push("+bindless".into());
} }
target_features.extend(builder.capabilities.iter().map(|cap| format!("+{:?}", cap)));
target_features.extend(builder.extensions.iter().map(|ext| format!("+ext:{}", ext)));
let feature_flag = if target_features.is_empty() { let feature_flag = if target_features.is_empty() {
String::new() String::new()

View File

@ -1,3 +1,7 @@
// Hack: u8 requires the Int8 capability, so instead of compiling this to a u8, compile it to a
// u32. It's a little less efficient, but doesn't require the Int8 cap (and Arrayed values
// shouldn't be stored in memory anyway, so it's no less memory used)
#[repr(u32)]
/// The access permissions for the image. /// The access permissions for the image.
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum AccessQualifier { pub enum AccessQualifier {
@ -10,6 +14,7 @@ pub enum AccessQualifier {
} }
/// Whether the image uses arrayed content. /// Whether the image uses arrayed content.
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum Arrayed { pub enum Arrayed {
/// The image uses not arrayed content. /// The image uses not arrayed content.
@ -18,6 +23,7 @@ pub enum Arrayed {
True = 1, True = 1,
} }
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
impl From<bool> for Arrayed { impl From<bool> for Arrayed {
fn from(val: bool) -> Self { fn from(val: bool) -> Self {
if val { if val {
@ -29,6 +35,7 @@ impl From<bool> for Arrayed {
} }
/// The dimension of the image. /// The dimension of the image.
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum Dimensionality { pub enum Dimensionality {
/// 1D /// 1D
@ -52,6 +59,7 @@ pub enum Dimensionality {
/// type. /// type.
/// ///
/// [depth]: https://en.wikipedia.org/wiki/Depth_map /// [depth]: https://en.wikipedia.org/wiki/Depth_map
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum ImageDepth { pub enum ImageDepth {
/// Indicates that the image does not contain depth information. /// Indicates that the image does not contain depth information.
@ -63,6 +71,7 @@ pub enum ImageDepth {
Unknown = 2, Unknown = 2,
} }
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
impl From<Option<bool>> for ImageDepth { impl From<Option<bool>> for ImageDepth {
fn from(val: Option<bool>) -> Self { fn from(val: Option<bool>) -> Self {
match val { match val {
@ -73,6 +82,7 @@ impl From<Option<bool>> for ImageDepth {
} }
} }
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
impl From<bool> for ImageDepth { impl From<bool> for ImageDepth {
fn from(val: bool) -> Self { fn from(val: bool) -> Self {
match val { match val {
@ -83,6 +93,7 @@ impl From<bool> for ImageDepth {
} }
/// Whether the image uses arrayed content. /// Whether the image uses arrayed content.
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum Multisampled { pub enum Multisampled {
/// The image contains single-sampled content. /// The image contains single-sampled content.
@ -91,6 +102,7 @@ pub enum Multisampled {
True = 1, True = 1,
} }
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
impl From<bool> for Multisampled { impl From<bool> for Multisampled {
fn from(val: bool) -> Self { fn from(val: bool) -> Self {
if val { if val {
@ -102,6 +114,7 @@ impl From<bool> for Multisampled {
} }
/// Whether or not the image will be accessed in combination with a sampler. /// Whether or not the image will be accessed in combination with a sampler.
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum Sampled { pub enum Sampled {
/// Indicates that it is not known ahead of time whether the image will use /// Indicates that it is not known ahead of time whether the image will use
@ -113,6 +126,7 @@ pub enum Sampled {
No = 2, No = 2,
} }
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
impl From<Option<bool>> for Sampled { impl From<Option<bool>> for Sampled {
fn from(val: Option<bool>) -> Self { fn from(val: Option<bool>) -> Self {
match val { match val {
@ -123,6 +137,7 @@ impl From<Option<bool>> for Sampled {
} }
} }
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
impl From<bool> for Sampled { impl From<bool> for Sampled {
fn from(val: bool) -> Self { fn from(val: bool) -> Self {
match val { match val {
@ -133,6 +148,7 @@ impl From<bool> for Sampled {
} }
/// The underlying internal representation of the image. /// The underlying internal representation of the image.
#[repr(u32)]
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum ImageFormat { pub enum ImageFormat {
/// Representation not known at compile time. /// Representation not known at compile time.

View File

@ -1,11 +1,19 @@
use spirv_builder::SpirvBuilder; use spirv_builder::{Capability, SpirvBuilder};
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
fn build_shader(path_to_create: &str, codegen_names: bool) -> Result<(), Box<dyn Error>> { fn build_shader(
let result = SpirvBuilder::new(path_to_create, "spirv-unknown-vulkan1.0").build()?; path_to_create: &str,
codegen_names: bool,
caps: &[Capability],
) -> Result<(), Box<dyn Error>> {
let mut builder = SpirvBuilder::new(path_to_create, "spirv-unknown-vulkan1.0");
for &cap in caps {
builder = builder.capability(cap);
}
let result = builder.build()?;
if codegen_names { if codegen_names {
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("entry_points.rs"); let dest_path = Path::new(&out_dir).join("entry_points.rs");
@ -15,9 +23,13 @@ fn build_shader(path_to_create: &str, codegen_names: bool) -> Result<(), Box<dyn
} }
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
build_shader("../../shaders/sky-shader", true)?; build_shader("../../shaders/sky-shader", true, &[])?;
build_shader("../../shaders/simplest-shader", false)?; build_shader("../../shaders/simplest-shader", false, &[])?;
build_shader("../../shaders/compute-shader", false)?; build_shader("../../shaders/compute-shader", false, &[])?;
build_shader("../../shaders/mouse-shader", false)?; build_shader(
"../../shaders/mouse-shader",
false,
&[Capability::Int8, Capability::Int16, Capability::Int64],
)?;
Ok(()) Ok(())
} }

View File

@ -59,7 +59,7 @@ pub enum RustGPUShader {
fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleDescriptor<'static> { fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleDescriptor<'static> {
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))] #[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
{ {
use spirv_builder::SpirvBuilder; use spirv_builder::{Capability, SpirvBuilder};
use std::borrow::Cow; use std::borrow::Cow;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
// Hack: spirv_builder builds into a custom directory if running under cargo, to not // Hack: spirv_builder builds into a custom directory if running under cargo, to not
@ -70,11 +70,14 @@ fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleDescriptor<'static>
// under cargo by setting these environment variables. // under cargo by setting these environment variables.
std::env::set_var("OUT_DIR", env!("OUT_DIR")); std::env::set_var("OUT_DIR", env!("OUT_DIR"));
std::env::set_var("PROFILE", env!("PROFILE")); std::env::set_var("PROFILE", env!("PROFILE"));
let crate_name = match shader { let (crate_name, capabilities): (_, &[Capability]) = match shader {
RustGPUShader::Simplest => "sky-shader", RustGPUShader::Simplest => ("sky-shader", &[]),
RustGPUShader::Sky => "simplest-shader", RustGPUShader::Sky => ("simplest-shader", &[]),
RustGPUShader::Compute => "compute-shader", RustGPUShader::Compute => ("compute-shader", &[]),
RustGPUShader::Mouse => "mouse-shader", RustGPUShader::Mouse => (
"mouse-shader",
&[Capability::Int8, Capability::Int16, Capability::Int64],
),
}; };
let manifest_dir = env!("CARGO_MANIFEST_DIR"); let manifest_dir = env!("CARGO_MANIFEST_DIR");
let crate_path = [ let crate_path = [
@ -87,10 +90,12 @@ fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleDescriptor<'static>
.iter() .iter()
.copied() .copied()
.collect::<PathBuf>(); .collect::<PathBuf>();
let compile_result = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.0") let mut builder =
.print_metadata(false) SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.0").print_metadata(false);
.build() for &cap in capabilities {
.unwrap(); builder = builder.capability(cap);
}
let compile_result = builder.build().unwrap();
let module_path = compile_result.module.unwrap_single(); let module_path = compile_result.module.unwrap_single();
let data = std::fs::read(module_path).unwrap(); let data = std::fs::read(module_path).unwrap();
let spirv = wgpu::util::make_spirv(&data); let spirv = wgpu::util::make_spirv(&data);

View File

@ -299,6 +299,7 @@ fn rust_flags(codegen_backend_path: &Path) -> String {
"-Cdebug-assertions=off", "-Cdebug-assertions=off",
"-Cdebuginfo=2", "-Cdebuginfo=2",
"-Cembed-bitcode=no", "-Cembed-bitcode=no",
"-Ctarget-feature=+Int8,+Int16,+Int64,+Float64",
] ]
.join(" ") .join(" ")
} }

View File

@ -1,3 +1,7 @@
OpCapability Float64
OpCapability Int16
OpCapability Int64
OpCapability Int8
OpCapability RuntimeDescriptorArray OpCapability RuntimeDescriptorArray
OpCapability Shader OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_EXT_descriptor_indexing"

View File

@ -1,3 +1,7 @@
OpCapability Float64
OpCapability Int16
OpCapability Int64
OpCapability Int8
OpCapability Shader OpCapability Shader
OpMemoryModel Logical Simple OpMemoryModel Logical Simple
OpEntryPoint Fragment %1 "hello_world" OpEntryPoint Fragment %1 "hello_world"

View File

@ -1,5 +1,4 @@
// build-pass // build-pass
//
// compile-flags: -C target-feature=+StorageImageWriteWithoutFormat // compile-flags: -C target-feature=+StorageImageWriteWithoutFormat
use glam::*; use glam::*;