mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-22 06:45:13 +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::Span;
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
|
||||
use rustc_target::abi::{
|
||||
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>(
|
||||
cx: &CodegenCx<'tcx>,
|
||||
span: Span,
|
||||
mut span: Span,
|
||||
ty: TyAndLayout<'tcx>,
|
||||
is_immediate: bool,
|
||||
) -> Word {
|
||||
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));
|
||||
|
||||
if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
use super::Builder;
|
||||
use crate::builder_spirv::{SpirvValue, SpirvValueExt};
|
||||
use crate::codegen_cx::CodegenCx;
|
||||
use rspirv::spirv::{CLOp, GLOp, Word};
|
||||
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 {
|
||||
assert!(!cx.target.is_kernel());
|
||||
assert!(!bx.target.is_kernel());
|
||||
self.integer_functions_2_intel = true;
|
||||
cx.emit_global()
|
||||
.extension("SPV_INTEL_shader_integer_functions2");
|
||||
cx.emit_global()
|
||||
.capability(Capability::IntegerFunctions2INTEL);
|
||||
if !bx
|
||||
.builder
|
||||
.has_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() {
|
||||
self.cl_op(CLOp::clz, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.ext_inst
|
||||
.borrow_mut()
|
||||
.import_integer_functions_2_intel(self);
|
||||
self.emit()
|
||||
let result = self
|
||||
.emit()
|
||||
.u_count_leading_zeros_intel(
|
||||
args[0].immediate().ty,
|
||||
None,
|
||||
args[0].immediate().def(self),
|
||||
)
|
||||
.unwrap()
|
||||
.with_type(args[0].immediate().ty)
|
||||
.unwrap();
|
||||
self.ext_inst
|
||||
.borrow_mut()
|
||||
.require_integer_functions_2_intel(self, result);
|
||||
result.with_type(args[0].immediate().ty)
|
||||
}
|
||||
}
|
||||
sym::cttz | sym::cttz_nonzero => {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::ctz, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.ext_inst
|
||||
.borrow_mut()
|
||||
.import_integer_functions_2_intel(self);
|
||||
self.emit()
|
||||
let result = self
|
||||
.emit()
|
||||
.u_count_trailing_zeros_intel(
|
||||
args[0].immediate().ty,
|
||||
None,
|
||||
args[0].immediate().def(self),
|
||||
)
|
||||
.unwrap()
|
||||
.with_type(args[0].immediate().ty)
|
||||
.unwrap();
|
||||
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
|
||||
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() {
|
||||
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 {
|
||||
let mut builder = self.builder.borrow_mut();
|
||||
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 {
|
||||
let sym = Symbols::get();
|
||||
|
||||
let features = tcx
|
||||
let mut feature_names = tcx
|
||||
.sess
|
||||
.target_features
|
||||
.iter()
|
||||
.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>>()
|
||||
.unwrap_or_else(|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.libm
|
||||
|| 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`?
|
||||
|
@ -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");
|
||||
// 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);
|
||||
if let Some(params) = fn_parameters.get(&import_id) {
|
||||
for ¶m in params {
|
||||
@ -108,11 +108,56 @@ fn fn_parameters(module: &Module) -> FxHashMap<Word, Vec<Word>> {
|
||||
.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 {
|
||||
Ok(())
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
mod capability_computation;
|
||||
mod dce;
|
||||
mod duplicates;
|
||||
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");
|
||||
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 {
|
||||
let _timer = sess.timer("link_compact_ids");
|
||||
|
@ -224,7 +224,7 @@ fn type_mismatch() {
|
||||
let result = assemble_and_link(&[&a, &b]);
|
||||
assert_eq!(
|
||||
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 libm: Symbol,
|
||||
pub num_traits: Symbol,
|
||||
pub glam: Symbol,
|
||||
pub entry_point_name: Symbol,
|
||||
descriptor_set: Symbol,
|
||||
binding: Symbol,
|
||||
@ -374,6 +375,7 @@ impl Symbols {
|
||||
spirv_std: Symbol::intern("spirv_std"),
|
||||
libm: Symbol::intern("libm"),
|
||||
num_traits: Symbol::intern("num_traits"),
|
||||
glam: Symbol::intern("glam"),
|
||||
descriptor_set: Symbol::intern("descriptor_set"),
|
||||
binding: Symbol::intern("binding"),
|
||||
image_type: Symbol::intern("image_type"),
|
||||
|
@ -66,6 +66,7 @@ use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
pub use rustc_codegen_spirv::rspirv::spirv::Capability;
|
||||
pub use rustc_codegen_spirv::{CompileResult, ModuleResult};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -112,6 +113,8 @@ pub struct SpirvBuilder {
|
||||
target: String,
|
||||
bindless: bool,
|
||||
multimodule: bool,
|
||||
capabilities: Vec<Capability>,
|
||||
extensions: Vec<String>,
|
||||
}
|
||||
|
||||
impl SpirvBuilder {
|
||||
@ -123,6 +126,8 @@ impl SpirvBuilder {
|
||||
target: target.into(),
|
||||
bindless: false,
|
||||
multimodule: false,
|
||||
capabilities: Vec::new(),
|
||||
extensions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,6 +158,20 @@ impl SpirvBuilder {
|
||||
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
|
||||
/// in the result, as the environment variable for the path to the module will already be set.
|
||||
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();
|
||||
|
||||
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() {
|
||||
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.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum AccessQualifier {
|
||||
@ -10,6 +14,7 @@ pub enum AccessQualifier {
|
||||
}
|
||||
|
||||
/// Whether the image uses arrayed content.
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Arrayed {
|
||||
/// The image uses not arrayed content.
|
||||
@ -18,6 +23,7 @@ pub enum Arrayed {
|
||||
True = 1,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<bool> for Arrayed {
|
||||
fn from(val: bool) -> Self {
|
||||
if val {
|
||||
@ -29,6 +35,7 @@ impl From<bool> for Arrayed {
|
||||
}
|
||||
|
||||
/// The dimension of the image.
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Dimensionality {
|
||||
/// 1D
|
||||
@ -52,6 +59,7 @@ pub enum Dimensionality {
|
||||
/// type.
|
||||
///
|
||||
/// [depth]: https://en.wikipedia.org/wiki/Depth_map
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ImageDepth {
|
||||
/// Indicates that the image does not contain depth information.
|
||||
@ -63,6 +71,7 @@ pub enum ImageDepth {
|
||||
Unknown = 2,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<Option<bool>> for ImageDepth {
|
||||
fn from(val: Option<bool>) -> Self {
|
||||
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 {
|
||||
fn from(val: bool) -> Self {
|
||||
match val {
|
||||
@ -83,6 +93,7 @@ impl From<bool> for ImageDepth {
|
||||
}
|
||||
|
||||
/// Whether the image uses arrayed content.
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Multisampled {
|
||||
/// The image contains single-sampled content.
|
||||
@ -91,6 +102,7 @@ pub enum Multisampled {
|
||||
True = 1,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<bool> for Multisampled {
|
||||
fn from(val: bool) -> Self {
|
||||
if val {
|
||||
@ -102,6 +114,7 @@ impl From<bool> for Multisampled {
|
||||
}
|
||||
|
||||
/// Whether or not the image will be accessed in combination with a sampler.
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Sampled {
|
||||
/// Indicates that it is not known ahead of time whether the image will use
|
||||
@ -113,6 +126,7 @@ pub enum Sampled {
|
||||
No = 2,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<Option<bool>> for Sampled {
|
||||
fn from(val: Option<bool>) -> Self {
|
||||
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 {
|
||||
fn from(val: bool) -> Self {
|
||||
match val {
|
||||
@ -133,6 +148,7 @@ impl From<bool> for Sampled {
|
||||
}
|
||||
|
||||
/// The underlying internal representation of the image.
|
||||
#[repr(u32)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ImageFormat {
|
||||
/// Representation not known at compile time.
|
||||
|
@ -1,11 +1,19 @@
|
||||
use spirv_builder::SpirvBuilder;
|
||||
use spirv_builder::{Capability, SpirvBuilder};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn build_shader(path_to_create: &str, codegen_names: bool) -> Result<(), Box<dyn Error>> {
|
||||
let result = SpirvBuilder::new(path_to_create, "spirv-unknown-vulkan1.0").build()?;
|
||||
fn build_shader(
|
||||
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 {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
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>> {
|
||||
build_shader("../../shaders/sky-shader", true)?;
|
||||
build_shader("../../shaders/simplest-shader", false)?;
|
||||
build_shader("../../shaders/compute-shader", false)?;
|
||||
build_shader("../../shaders/mouse-shader", false)?;
|
||||
build_shader("../../shaders/sky-shader", true, &[])?;
|
||||
build_shader("../../shaders/simplest-shader", false, &[])?;
|
||||
build_shader("../../shaders/compute-shader", false, &[])?;
|
||||
build_shader(
|
||||
"../../shaders/mouse-shader",
|
||||
false,
|
||||
&[Capability::Int8, Capability::Int16, Capability::Int64],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ pub enum RustGPUShader {
|
||||
fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleDescriptor<'static> {
|
||||
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
|
||||
{
|
||||
use spirv_builder::SpirvBuilder;
|
||||
use spirv_builder::{Capability, SpirvBuilder};
|
||||
use std::borrow::Cow;
|
||||
use std::path::{Path, PathBuf};
|
||||
// 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.
|
||||
std::env::set_var("OUT_DIR", env!("OUT_DIR"));
|
||||
std::env::set_var("PROFILE", env!("PROFILE"));
|
||||
let crate_name = match shader {
|
||||
RustGPUShader::Simplest => "sky-shader",
|
||||
RustGPUShader::Sky => "simplest-shader",
|
||||
RustGPUShader::Compute => "compute-shader",
|
||||
RustGPUShader::Mouse => "mouse-shader",
|
||||
let (crate_name, capabilities): (_, &[Capability]) = match shader {
|
||||
RustGPUShader::Simplest => ("sky-shader", &[]),
|
||||
RustGPUShader::Sky => ("simplest-shader", &[]),
|
||||
RustGPUShader::Compute => ("compute-shader", &[]),
|
||||
RustGPUShader::Mouse => (
|
||||
"mouse-shader",
|
||||
&[Capability::Int8, Capability::Int16, Capability::Int64],
|
||||
),
|
||||
};
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let crate_path = [
|
||||
@ -87,10 +90,12 @@ fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleDescriptor<'static>
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<PathBuf>();
|
||||
let compile_result = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.0")
|
||||
.print_metadata(false)
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut builder =
|
||||
SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.0").print_metadata(false);
|
||||
for &cap in capabilities {
|
||||
builder = builder.capability(cap);
|
||||
}
|
||||
let compile_result = builder.build().unwrap();
|
||||
let module_path = compile_result.module.unwrap_single();
|
||||
let data = std::fs::read(module_path).unwrap();
|
||||
let spirv = wgpu::util::make_spirv(&data);
|
||||
|
@ -299,6 +299,7 @@ fn rust_flags(codegen_backend_path: &Path) -> String {
|
||||
"-Cdebug-assertions=off",
|
||||
"-Cdebuginfo=2",
|
||||
"-Cembed-bitcode=no",
|
||||
"-Ctarget-feature=+Int8,+Int16,+Int64,+Float64",
|
||||
]
|
||||
.join(" ")
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
OpCapability Float64
|
||||
OpCapability Int16
|
||||
OpCapability Int64
|
||||
OpCapability Int8
|
||||
OpCapability RuntimeDescriptorArray
|
||||
OpCapability Shader
|
||||
OpExtension "SPV_EXT_descriptor_indexing"
|
||||
|
@ -1,3 +1,7 @@
|
||||
OpCapability Float64
|
||||
OpCapability Int16
|
||||
OpCapability Int64
|
||||
OpCapability Int8
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Fragment %1 "hello_world"
|
||||
|
@ -1,5 +1,4 @@
|
||||
// build-pass
|
||||
//
|
||||
// compile-flags: -C target-feature=+StorageImageWriteWithoutFormat
|
||||
|
||||
use glam::*;
|
||||
|
Loading…
Reference in New Issue
Block a user