mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-25 16:25:25 +00:00
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:
parent
255a6f09e6
commit
6019f391ec
@ -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) {
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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`?
|
||||||
|
@ -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())
|
|
||||||
})
|
|
||||||
}
|
|
@ -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 ¶m in params {
|
for ¶m 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
@ -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)")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"),
|
||||||
|
@ -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()
|
||||||
|
@ -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.
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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(" ")
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// build-pass
|
// build-pass
|
||||||
//
|
|
||||||
// compile-flags: -C target-feature=+StorageImageWriteWithoutFormat
|
// compile-flags: -C target-feature=+StorageImageWriteWithoutFormat
|
||||||
|
|
||||||
use glam::*;
|
use glam::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user