mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-21 22:34:34 +00:00
Add new handling for SPIR-V targets (#559)
* Add new handling for SPIR-V targets * Ensure target is always three components
This commit is contained in:
parent
4824d205ba
commit
b6311789bb
2
.github/workflows/test.sh
vendored
2
.github/workflows/test.sh
vendored
@ -52,4 +52,4 @@ cargo_test_no_features examples/runners/cpu
|
||||
cargo_test_no_features examples/shaders/sky-shader
|
||||
cargo_test_no_features examples/shaders/simplest-shader
|
||||
|
||||
cargo compiletest --target-env unknown,vulkan1.1,spv1.3
|
||||
cargo compiletest --target-env vulkan1.1,spv1.3
|
||||
|
@ -415,7 +415,7 @@ fn trans_scalar<'tcx>(
|
||||
|
||||
match scalar.value {
|
||||
Primitive::Int(width, mut signedness) => {
|
||||
if cx.kernel_mode {
|
||||
if cx.target.is_kernel() {
|
||||
signedness = false;
|
||||
}
|
||||
SpirvType::Integer(width.size().bits() as u32, signedness).def(span, cx)
|
||||
@ -644,7 +644,7 @@ pub fn auto_struct_layout<'tcx>(
|
||||
fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
|
||||
if let TyKind::Foreign(_) = ty.ty.kind() {
|
||||
// "An unsized FFI type that is opaque to Rust", `extern type A;` (currently unstable)
|
||||
if cx.kernel_mode {
|
||||
if cx.target.is_kernel() {
|
||||
// TODO: This should use the name of the struct as the name. However, names are not stable across crates,
|
||||
// e.g. core::fmt::Opaque in one crate and fmt::Opaque in core.
|
||||
return SpirvType::Opaque {
|
||||
|
@ -17,7 +17,7 @@ pub struct ExtInst {
|
||||
|
||||
impl ExtInst {
|
||||
pub fn import_glsl<'a, 'tcx>(&mut self, bx: &Builder<'a, 'tcx>) -> Word {
|
||||
assert!(!bx.kernel_mode);
|
||||
assert!(!bx.target.is_kernel());
|
||||
match self.glsl {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
@ -29,7 +29,7 @@ impl ExtInst {
|
||||
}
|
||||
|
||||
pub fn import_opencl<'a, 'tcx>(&mut self, bx: &Builder<'a, 'tcx>) -> Word {
|
||||
assert!(bx.kernel_mode);
|
||||
assert!(bx.target.is_kernel());
|
||||
match self.opencl {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
@ -42,7 +42,7 @@ impl ExtInst {
|
||||
|
||||
pub fn import_integer_functions_2_intel<'tcx>(&mut self, cx: &CodegenCx<'tcx>) {
|
||||
if !self.integer_functions_2_intel {
|
||||
assert!(!cx.kernel_mode);
|
||||
assert!(!cx.target.is_kernel());
|
||||
self.integer_functions_2_intel = true;
|
||||
cx.emit_global()
|
||||
.extension("SPV_INTEL_shader_integer_functions2");
|
||||
|
@ -154,14 +154,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
|
||||
// TODO: Configure these to be ocl vs. gl ext instructions, etc.
|
||||
sym::sqrtf32 | sym::sqrtf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::sqrt, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Sqrt, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::powif32 | sym::powif64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(
|
||||
CLOp::pown,
|
||||
ret_ty,
|
||||
@ -173,21 +173,21 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::sinf32 | sym::sinf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::sin, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Sin, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::cosf32 | sym::cosf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::cos, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Cos, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::powf32 | sym::powf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(
|
||||
CLOp::pow,
|
||||
ret_ty,
|
||||
@ -202,35 +202,35 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::expf32 | sym::expf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::exp, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Exp, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::exp2f32 | sym::exp2f64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::exp2, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Exp2, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::logf32 | sym::logf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::log, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Log, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::log2f32 | sym::log2f64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::log2, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Log2, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::log10f32 | sym::log10f64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::log10, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
// spir-v glsl doesn't have log10, so,
|
||||
@ -241,7 +241,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::fmaf32 | sym::fmaf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(
|
||||
CLOp::fma,
|
||||
ret_ty,
|
||||
@ -264,14 +264,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::fabsf32 | sym::fabsf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::fabs, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::FAbs, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::minnumf32 | sym::minnumf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(
|
||||
CLOp::fmin,
|
||||
ret_ty,
|
||||
@ -286,7 +286,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::maxnumf32 | sym::maxnumf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(
|
||||
CLOp::fmax,
|
||||
ret_ty,
|
||||
@ -301,7 +301,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::copysignf32 | sym::copysignf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(
|
||||
CLOp::copysign,
|
||||
ret_ty,
|
||||
@ -314,21 +314,21 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::floorf32 | sym::floorf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::floor, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Floor, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::ceilf32 | sym::ceilf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::ceil, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Ceil, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::truncf32 | sym::truncf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::trunc, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Trunc, ret_ty, [args[0].immediate()])
|
||||
@ -336,14 +336,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
// TODO: Correctness of all these rounds
|
||||
sym::rintf32 | sym::rintf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::rint, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Round, ret_ty, [args[0].immediate()])
|
||||
}
|
||||
}
|
||||
sym::nearbyintf32 | sym::nearbyintf64 | sym::roundf32 | sym::roundf64 => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::round, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.gl_op(GLOp::Round, ret_ty, [args[0].immediate()])
|
||||
@ -359,7 +359,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
|
||||
// TODO: Do we want to manually implement these instead of using intel instructions?
|
||||
sym::ctlz | sym::ctlz_nonzero => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::clz, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.ext_inst
|
||||
@ -376,7 +376,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
sym::cttz | sym::cttz_nonzero => {
|
||||
if self.kernel_mode {
|
||||
if self.target.is_kernel() {
|
||||
self.cl_op(CLOp::ctz, ret_ty, [args[0].immediate()])
|
||||
} else {
|
||||
self.ext_inst
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::builder;
|
||||
use crate::codegen_cx::CodegenCx;
|
||||
use crate::spirv_type::SpirvType;
|
||||
use crate::target::SpirvTarget;
|
||||
use rspirv::dr::{Block, Builder, Module, Operand};
|
||||
use rspirv::spirv::{AddressingModel, Capability, MemoryModel, Op, StorageClass, Word};
|
||||
use rspirv::{binary::Assemble, binary::Disassemble};
|
||||
@ -302,17 +303,14 @@ pub struct BuilderSpirv {
|
||||
}
|
||||
|
||||
impl BuilderSpirv {
|
||||
pub fn new(
|
||||
version: Option<(u8, u8)>,
|
||||
memory_model: Option<MemoryModel>,
|
||||
kernel_mode: bool,
|
||||
) -> Self {
|
||||
pub fn new(target: &SpirvTarget) -> Self {
|
||||
let version = target.spirv_version();
|
||||
let memory_model = target.memory_model();
|
||||
|
||||
let mut builder = Builder::new();
|
||||
// Default to spir-v 1.3
|
||||
let version = version.unwrap_or((1, 3));
|
||||
builder.set_version(version.0, version.1);
|
||||
let memory_model = memory_model.unwrap_or(MemoryModel::Vulkan);
|
||||
if kernel_mode {
|
||||
|
||||
if target.is_kernel() {
|
||||
builder.capability(Capability::Kernel);
|
||||
} else {
|
||||
builder.capability(Capability::Shader);
|
||||
@ -327,18 +325,23 @@ impl BuilderSpirv {
|
||||
builder.extension("SPV_KHR_variable_pointers");
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
if kernel_mode {
|
||||
|
||||
let addressing_model = if target.is_kernel() {
|
||||
builder.capability(Capability::Addresses);
|
||||
builder.memory_model(AddressingModel::Physical32, MemoryModel::OpenCL);
|
||||
AddressingModel::Physical32
|
||||
} else {
|
||||
builder.memory_model(AddressingModel::Logical, memory_model);
|
||||
}
|
||||
AddressingModel::Logical
|
||||
};
|
||||
|
||||
builder.memory_model(addressing_model, memory_model);
|
||||
|
||||
Self {
|
||||
builder: RefCell::new(builder),
|
||||
const_to_id: Default::default(),
|
||||
|
@ -25,7 +25,7 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
}
|
||||
|
||||
pub fn constant_i32(&self, span: Span, val: i32) -> SpirvValue {
|
||||
let ty = SpirvType::Integer(32, !self.kernel_mode).def(span, self);
|
||||
let ty = SpirvType::Integer(32, !self.target.is_kernel()).def(span, self);
|
||||
self.builder.def_constant(ty, SpirvConst::U32(val as u32))
|
||||
}
|
||||
|
||||
@ -219,7 +219,7 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
|
||||
Primitive::Int(int_size, int_signedness) => match self.lookup_type(ty) {
|
||||
SpirvType::Integer(width, spirv_signedness) => {
|
||||
assert_eq!(width as u64, int_size.size().bits());
|
||||
if !self.kernel_mode {
|
||||
if !self.target.is_kernel() {
|
||||
assert_eq!(spirv_signedness, int_signedness);
|
||||
}
|
||||
self.constant_int(ty, data as u64)
|
||||
|
@ -10,8 +10,10 @@ use crate::decorations::{
|
||||
};
|
||||
use crate::spirv_type::{SpirvType, SpirvTypePrinter, TypeCache};
|
||||
use crate::symbols::Symbols;
|
||||
use crate::target::SpirvTarget;
|
||||
|
||||
use rspirv::dr::{Module, Operand};
|
||||
use rspirv::spirv::{AddressingModel, Decoration, LinkageType, MemoryModel, Word};
|
||||
use rspirv::spirv::{AddressingModel, Decoration, LinkageType, Word};
|
||||
use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind};
|
||||
use rustc_codegen_ssa::traits::{
|
||||
AsmMethods, BackendTypes, CoverageInfoMethods, DebugInfoMethods, MiscMethods,
|
||||
@ -55,7 +57,6 @@ pub struct CodegenCx<'tcx> {
|
||||
/// get `LoopControl::UNROLL` applied to all of their loops' `OpLoopMerge`
|
||||
/// instructions, during structuralization.
|
||||
unroll_loops_decorations: RefCell<FxHashMap<Word, UnrollLoopsDecoration>>,
|
||||
pub kernel_mode: bool,
|
||||
/// Cache of all the builtin symbols we need
|
||||
pub sym: Rc<Symbols>,
|
||||
pub instruction_table: InstructionTable,
|
||||
@ -71,44 +72,24 @@ pub struct CodegenCx<'tcx> {
|
||||
pub i8_i16_atomics_allowed: bool,
|
||||
|
||||
pub codegen_args: CodegenArgs,
|
||||
|
||||
/// Information about the SPIR-V target.
|
||||
pub target: SpirvTarget,
|
||||
}
|
||||
|
||||
impl<'tcx> CodegenCx<'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>) -> Self {
|
||||
let sym = Symbols::get();
|
||||
let mut spirv_version = None;
|
||||
let mut memory_model = None;
|
||||
let mut kernel_mode = false;
|
||||
for &feature in &tcx.sess.target_features {
|
||||
if feature == sym.kernel {
|
||||
kernel_mode = true;
|
||||
} else if feature == sym.spirv10 {
|
||||
spirv_version = Some((1, 0));
|
||||
} else if feature == sym.spirv11 {
|
||||
spirv_version = Some((1, 1));
|
||||
} else if feature == sym.spirv12 {
|
||||
spirv_version = Some((1, 2));
|
||||
} else if feature == sym.spirv13 {
|
||||
spirv_version = Some((1, 3));
|
||||
} else if feature == sym.spirv14 {
|
||||
spirv_version = Some((1, 4));
|
||||
} else if feature == sym.spirv15 {
|
||||
spirv_version = Some((1, 5));
|
||||
} else if feature == sym.simple {
|
||||
memory_model = Some(MemoryModel::Simple);
|
||||
} else if feature == sym.vulkan {
|
||||
memory_model = Some(MemoryModel::Vulkan);
|
||||
} else if feature == sym.glsl450 {
|
||||
memory_model = Some(MemoryModel::GLSL450);
|
||||
} else {
|
||||
tcx.sess.err(&format!("Unknown feature {}", feature));
|
||||
}
|
||||
tcx.sess.err(&format!("Unknown feature {}", feature));
|
||||
}
|
||||
let codegen_args = CodegenArgs::from_session(tcx.sess);
|
||||
let target = tcx.sess.target.llvm_target.parse().unwrap();
|
||||
|
||||
Self {
|
||||
tcx,
|
||||
codegen_unit,
|
||||
builder: BuilderSpirv::new(spirv_version, memory_model, kernel_mode),
|
||||
builder: BuilderSpirv::new(&target),
|
||||
instances: Default::default(),
|
||||
function_parameter_values: Default::default(),
|
||||
type_cache: Default::default(),
|
||||
@ -116,7 +97,7 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
ext_inst: Default::default(),
|
||||
zombie_decorations: Default::default(),
|
||||
unroll_loops_decorations: Default::default(),
|
||||
kernel_mode,
|
||||
target,
|
||||
sym,
|
||||
instruction_table: InstructionTable::new(),
|
||||
libm_intrinsics: Default::default(),
|
||||
|
@ -116,6 +116,7 @@ mod linker;
|
||||
mod spirv_type;
|
||||
mod spirv_type_constraints;
|
||||
mod symbols;
|
||||
mod target;
|
||||
|
||||
use builder::Builder;
|
||||
use codegen_cx::{CodegenArgs, CodegenCx, ModuleOutputType};
|
||||
@ -146,7 +147,7 @@ use rustc_session::config::{self, OptLevel, OutputFilenames, OutputType};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetTriple};
|
||||
use rustc_target::spec::{Target, TargetTriple};
|
||||
use std::any::Any;
|
||||
use std::env;
|
||||
use std::fs::{create_dir_all, File};
|
||||
@ -211,34 +212,6 @@ fn is_blocklisted_fn<'tcx>(
|
||||
false
|
||||
}
|
||||
|
||||
fn target_options(env: Option<spirv_tools::TargetEnv>) -> Target {
|
||||
Target {
|
||||
llvm_target: "no-llvm".to_string(),
|
||||
pointer_width: 32,
|
||||
data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".to_string(),
|
||||
arch: "spirv".to_string(),
|
||||
options: TargetOptions {
|
||||
simd_types_indirect: false,
|
||||
allows_weak_linkage: false,
|
||||
crt_static_allows_dylibs: true,
|
||||
dll_prefix: "".to_string(),
|
||||
dll_suffix: ".spv".to_string(),
|
||||
dynamic_linking: true,
|
||||
emit_debug_gdb_scripts: false,
|
||||
linker_flavor: LinkerFlavor::Ld,
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
os: "unknown".to_string(),
|
||||
env: env
|
||||
.as_ref()
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_else(|| "unknown".to_string()),
|
||||
// TODO: Investigate if main_needs_argc_argv is useful (for building exes)
|
||||
main_needs_argc_argv: false,
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should this store Vec or Module?
|
||||
struct SpirvModuleBuffer(Vec<u32>);
|
||||
|
||||
@ -294,20 +267,10 @@ impl CodegenBackend for SpirvCodegenBackend {
|
||||
|
||||
fn target_override(&self, opts: &config::Options) -> Option<Target> {
|
||||
match opts.target_triple {
|
||||
TargetTriple::TargetTriple(ref target_triple) => {
|
||||
const ARCH_VENDOR: &str = "spirv-unknown-";
|
||||
if !target_triple.starts_with(ARCH_VENDOR) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let env = &target_triple[ARCH_VENDOR.len()..];
|
||||
|
||||
match env.parse() {
|
||||
Ok(env) => Some(target_options(Some(env))),
|
||||
Err(_) if env == "unknown" => Some(target_options(None)),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
TargetTriple::TargetTriple(ref target) => target
|
||||
.parse::<target::SpirvTarget>()
|
||||
.map(|target| target.rustc_target())
|
||||
.ok(),
|
||||
TargetTriple::TargetPath(_) => None,
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ impl SpirvType {
|
||||
let id = emit.id();
|
||||
let result = emit.type_struct_id(Some(id), field_types.iter().cloned());
|
||||
// The struct size is only used in our own sizeof_in_bits() (used in e.g. ArrayStride decoration)
|
||||
if !cx.kernel_mode {
|
||||
if !cx.target.is_kernel() {
|
||||
// TODO: kernel mode can't do this??
|
||||
for (index, offset) in field_offsets.iter().copied().enumerate() {
|
||||
emit.member_decorate(
|
||||
@ -177,7 +177,7 @@ impl SpirvType {
|
||||
.expect("Element of sized array must be sized")
|
||||
.bytes();
|
||||
let result = cx.emit_global().type_array(element, count.def_cx(cx));
|
||||
if !cx.kernel_mode {
|
||||
if !cx.target.is_kernel() {
|
||||
// TODO: kernel mode can't do this??
|
||||
cx.emit_global().decorate(
|
||||
result,
|
||||
@ -200,7 +200,7 @@ impl SpirvType {
|
||||
Decoration::ArrayStride,
|
||||
iter::once(Operand::LiteralInt32(element_size as u32)),
|
||||
);
|
||||
if cx.kernel_mode {
|
||||
if cx.target.is_kernel() {
|
||||
cx.zombie_with_span(result, def_span, "RuntimeArray in kernel mode");
|
||||
}
|
||||
result
|
||||
|
@ -20,16 +20,6 @@ pub struct Symbols {
|
||||
pub spirv_std: Symbol,
|
||||
pub libm: Symbol,
|
||||
pub num_traits: Symbol,
|
||||
pub kernel: Symbol,
|
||||
pub simple: Symbol,
|
||||
pub vulkan: Symbol,
|
||||
pub glsl450: Symbol,
|
||||
pub spirv10: Symbol,
|
||||
pub spirv11: Symbol,
|
||||
pub spirv12: Symbol,
|
||||
pub spirv13: Symbol,
|
||||
pub spirv14: Symbol,
|
||||
pub spirv15: Symbol,
|
||||
pub entry_point_name: Symbol,
|
||||
descriptor_set: Symbol,
|
||||
binding: Symbol,
|
||||
@ -376,16 +366,6 @@ impl Symbols {
|
||||
spirv_std: Symbol::intern("spirv_std"),
|
||||
libm: Symbol::intern("libm"),
|
||||
num_traits: Symbol::intern("num_traits"),
|
||||
kernel: Symbol::intern("kernel"),
|
||||
simple: Symbol::intern("simple"),
|
||||
vulkan: Symbol::intern("vulkan"),
|
||||
glsl450: Symbol::intern("glsl450"),
|
||||
spirv10: Symbol::intern("spirv1.0"),
|
||||
spirv11: Symbol::intern("spirv1.1"),
|
||||
spirv12: Symbol::intern("spirv1.2"),
|
||||
spirv13: Symbol::intern("spirv1.3"),
|
||||
spirv14: Symbol::intern("spirv1.4"),
|
||||
spirv15: Symbol::intern("spirv1.5"),
|
||||
descriptor_set: Symbol::intern("descriptor_set"),
|
||||
binding: Symbol::intern("binding"),
|
||||
image_type: Symbol::intern("image_type"),
|
||||
|
149
crates/rustc_codegen_spirv/src/target.rs
Normal file
149
crates/rustc_codegen_spirv/src/target.rs
Normal file
@ -0,0 +1,149 @@
|
||||
use rspirv::spirv::MemoryModel;
|
||||
use rustc_target::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions};
|
||||
use spirv_tools::TargetEnv;
|
||||
|
||||
const ARCH: &str = "spirv";
|
||||
|
||||
pub struct SpirvTarget {
|
||||
env: TargetEnv,
|
||||
vendor: String,
|
||||
}
|
||||
|
||||
impl SpirvTarget {
|
||||
pub fn is_kernel(&self) -> bool {
|
||||
self.memory_model() == MemoryModel::OpenCL
|
||||
}
|
||||
|
||||
pub fn memory_model(&self) -> MemoryModel {
|
||||
match self.env {
|
||||
TargetEnv::Universal_1_0
|
||||
| TargetEnv::Universal_1_1
|
||||
| TargetEnv::Universal_1_2
|
||||
| TargetEnv::Universal_1_3
|
||||
| TargetEnv::Universal_1_4
|
||||
| TargetEnv::Universal_1_5 => MemoryModel::Simple,
|
||||
|
||||
TargetEnv::OpenGL_4_0
|
||||
| TargetEnv::OpenGL_4_1
|
||||
| TargetEnv::OpenGL_4_2
|
||||
| TargetEnv::OpenGL_4_3
|
||||
| TargetEnv::OpenGL_4_5 => MemoryModel::GLSL450,
|
||||
|
||||
TargetEnv::OpenCL_2_1
|
||||
| TargetEnv::OpenCL_2_2
|
||||
| TargetEnv::OpenCL_1_2
|
||||
| TargetEnv::OpenCLEmbedded_1_2
|
||||
| TargetEnv::OpenCL_2_0
|
||||
| TargetEnv::OpenCLEmbedded_2_0
|
||||
| TargetEnv::OpenCLEmbedded_2_1
|
||||
| TargetEnv::OpenCLEmbedded_2_2 => MemoryModel::OpenCL,
|
||||
|
||||
TargetEnv::Vulkan_1_0
|
||||
| TargetEnv::Vulkan_1_1
|
||||
| TargetEnv::WebGPU_0
|
||||
| TargetEnv::Vulkan_1_1_Spirv_1_4
|
||||
| TargetEnv::Vulkan_1_2 => MemoryModel::Vulkan,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spirv_version(&self) -> (u8, u8) {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self.env {
|
||||
TargetEnv::Universal_1_0 => (1, 0),
|
||||
TargetEnv::Universal_1_1 => (1, 1),
|
||||
TargetEnv::Universal_1_2 => (1, 2),
|
||||
TargetEnv::Universal_1_3 => (1, 3),
|
||||
TargetEnv::Universal_1_4 => (1, 4),
|
||||
TargetEnv::Universal_1_5 => (1, 5),
|
||||
|
||||
TargetEnv::OpenGL_4_0 => (1, 0),
|
||||
TargetEnv::OpenGL_4_1 => (1, 0),
|
||||
TargetEnv::OpenGL_4_2 => (1, 0),
|
||||
TargetEnv::OpenGL_4_3 => (1, 0),
|
||||
TargetEnv::OpenGL_4_5 => (1, 3),
|
||||
|
||||
TargetEnv::OpenCL_1_2 => (1, 0),
|
||||
TargetEnv::OpenCL_2_0 => (1, 0),
|
||||
TargetEnv::OpenCL_2_1 => (1, 0),
|
||||
TargetEnv::OpenCL_2_2 => (1, 2),
|
||||
TargetEnv::OpenCLEmbedded_1_2 => (1, 0),
|
||||
TargetEnv::OpenCLEmbedded_2_0 => (1, 0),
|
||||
TargetEnv::OpenCLEmbedded_2_1 => (1, 0),
|
||||
TargetEnv::OpenCLEmbedded_2_2 => (1, 2),
|
||||
|
||||
TargetEnv::Vulkan_1_0 => (1, 0),
|
||||
TargetEnv::Vulkan_1_1 => (1, 3),
|
||||
TargetEnv::WebGPU_0 => (1, 3),
|
||||
TargetEnv::Vulkan_1_1_Spirv_1_4 => (1, 4),
|
||||
TargetEnv::Vulkan_1_2 => (1, 5),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rustc_target(&self) -> Target {
|
||||
Target {
|
||||
llvm_target: self.to_string(),
|
||||
pointer_width: 32,
|
||||
data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".to_string(),
|
||||
arch: String::from(ARCH),
|
||||
options: TargetOptions {
|
||||
simd_types_indirect: false,
|
||||
allows_weak_linkage: false,
|
||||
crt_static_allows_dylibs: true,
|
||||
dll_prefix: "".to_string(),
|
||||
dll_suffix: ".spv".to_string(),
|
||||
dynamic_linking: true,
|
||||
emit_debug_gdb_scripts: false,
|
||||
linker_flavor: LinkerFlavor::Ld,
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
os: "unknown".to_string(),
|
||||
env: self.env.to_string(),
|
||||
vendor: self.vendor.clone(),
|
||||
// TODO: Investigate if main_needs_argc_argv is useful (for building exes)
|
||||
main_needs_argc_argv: false,
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for SpirvTarget {
|
||||
type Err = InvalidTarget;
|
||||
|
||||
fn from_str(target: &str) -> Result<Self, Self::Err> {
|
||||
let mut iter = target.split('-');
|
||||
let error = || InvalidTarget(target.into());
|
||||
|
||||
if iter.next().map_or(true, |arch| arch != ARCH) {
|
||||
return Err(error());
|
||||
}
|
||||
|
||||
let vendor = iter.next().map(From::from).ok_or_else(error)?;
|
||||
|
||||
let env = iter
|
||||
.next()
|
||||
.and_then(|env| env.parse().ok())
|
||||
.ok_or_else(error)?;
|
||||
|
||||
if iter.next().is_some() {
|
||||
return Err(error());
|
||||
}
|
||||
|
||||
Ok(Self { env, vendor })
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SpirvTarget {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}-{}-{}", ARCH, self.vendor, self.env)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidTarget(String);
|
||||
|
||||
impl std::error::Error for InvalidTarget {}
|
||||
impl std::fmt::Display for InvalidTarget {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Invalid target `{}`.", self.0)
|
||||
}
|
||||
}
|
@ -106,17 +106,16 @@ pub struct SpirvBuilder {
|
||||
path_to_crate: PathBuf,
|
||||
print_metadata: bool,
|
||||
release: bool,
|
||||
spirv_version: Option<(u8, u8)>,
|
||||
memory_model: Option<MemoryModel>,
|
||||
target: String,
|
||||
}
|
||||
|
||||
impl SpirvBuilder {
|
||||
pub fn new(path_to_crate: impl AsRef<Path>) -> Self {
|
||||
pub fn new(path_to_crate: impl AsRef<Path>, target: impl Into<String>) -> Self {
|
||||
Self {
|
||||
path_to_crate: path_to_crate.as_ref().to_owned(),
|
||||
print_metadata: true,
|
||||
release: true,
|
||||
spirv_version: None,
|
||||
memory_model: None,
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,18 +131,6 @@ impl SpirvBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the SPIR-V binary version to use. Defaults to v1.3.
|
||||
pub fn spirv_version(mut self, major: u8, minor: u8) -> Self {
|
||||
self.spirv_version = Some((major, minor));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the SPIR-V memory model. Defaults to Vulkan.
|
||||
pub fn memory_model(mut self, memory_model: MemoryModel) -> Self {
|
||||
self.memory_model = Some(memory_model);
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the module. Returns the path to the built spir-v file. If `print_metadata` is true,
|
||||
/// you usually don't have to inspect the path, as the environment variable will already be
|
||||
/// set.
|
||||
@ -212,35 +199,13 @@ fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result<PathBuf, Sp
|
||||
// rustc expects a full path, instead of a filename looked up via LD_LIBRARY_PATH, so we need
|
||||
// to copy cargo's understanding of library lookup and find the library and its full path.
|
||||
let rustc_codegen_spirv = find_rustc_codegen_spirv();
|
||||
let mut target_features = Vec::new();
|
||||
// these must match codegen_cx/mod.rs
|
||||
if let Some((major, minor)) = builder.spirv_version {
|
||||
target_features.push(format!("+spirv{}.{}", major, minor));
|
||||
}
|
||||
if let Some(memory_model) = &builder.memory_model {
|
||||
target_features.push(
|
||||
match memory_model {
|
||||
MemoryModel::Simple => "+simple",
|
||||
MemoryModel::Vulkan => "+vulkan",
|
||||
MemoryModel::GLSL450 => "+glsl450",
|
||||
}
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
let feature_flag = if target_features.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" -C target-feature={}", target_features.join(","))
|
||||
};
|
||||
let llvm_args = if multimodule {
|
||||
" -C llvm-args=--module-output=multiple"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let llvm_args = multimodule
|
||||
.then(|| " -C llvm-args=--module-output=multiple")
|
||||
.unwrap_or_default();
|
||||
|
||||
let rustflags = format!(
|
||||
"-Z codegen-backend={} -Z symbol-mangling-version=v0{}{}",
|
||||
"-Z codegen-backend={} -Z symbol-mangling-version=v0{}",
|
||||
rustc_codegen_spirv.display(),
|
||||
feature_flag,
|
||||
llvm_args,
|
||||
);
|
||||
let mut cargo = Command::new("cargo");
|
||||
@ -250,7 +215,7 @@ fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result<PathBuf, Sp
|
||||
"-Zbuild-std=core",
|
||||
"-Zbuild-std-features=compiler-builtins-mem",
|
||||
"--target",
|
||||
"spirv-unknown-unknown",
|
||||
&*builder.target,
|
||||
]);
|
||||
if builder.release {
|
||||
cargo.arg("--release");
|
||||
|
@ -27,10 +27,8 @@ fn custom_entry_point() {
|
||||
pub fn main() { }
|
||||
"#,
|
||||
r#"OpCapability Shader
|
||||
OpCapability VulkanMemoryModel
|
||||
OpCapability VariablePointers
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical Vulkan
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Fragment %1 "hello_world"
|
||||
OpExecutionMode %1 OriginUpperLeft
|
||||
OpName %2 "test_project::main"
|
||||
@ -83,7 +81,7 @@ fn asm() {
|
||||
asm!(
|
||||
"%int = OpTypeInt 32 0",
|
||||
"%scope = OpConstant %int 2",
|
||||
"%semantics = OpConstant %int 8452",
|
||||
"%semantics = OpConstant %int 16",
|
||||
"OpMemoryBarrier %scope %semantics",
|
||||
);
|
||||
}
|
||||
@ -173,12 +171,10 @@ fn asm_op_decorate() {
|
||||
add_decorate();
|
||||
}"#,
|
||||
r#"OpCapability Shader
|
||||
OpCapability VulkanMemoryModel
|
||||
OpCapability VariablePointers
|
||||
OpCapability RuntimeDescriptorArray
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpExtension "SPV_EXT_descriptor_indexing"
|
||||
OpMemoryModel Logical Vulkan
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Fragment %1 "main"
|
||||
OpExecutionMode %1 OriginUpperLeft
|
||||
OpName %2 "test_project::add_decorate"
|
||||
|
@ -79,7 +79,7 @@ fn setup(src: &str) -> Result<PathBuf, Box<dyn Error>> {
|
||||
|
||||
fn build(src: &str) -> PathBuf {
|
||||
let project = setup(src).expect("Failed to set up project");
|
||||
crate::SpirvBuilder::new(&project)
|
||||
crate::SpirvBuilder::new(&project, "spirv-unknown-spv1.3")
|
||||
.print_metadata(false)
|
||||
.release(false)
|
||||
.build()
|
||||
|
@ -25,6 +25,45 @@ The `rust-gpu` project currently supports a limited number of platforms and grap
|
||||
| WGPU | 0.6 | Primary | Uses a translation layer to Metal/DX12
|
||||
| OpenGL | ??? | Tertiary |
|
||||
|
||||
### SPIR-V Targets
|
||||
|
||||
- `spirv-unknown-spv1.0`
|
||||
- `spirv-unknown-spv1.1`
|
||||
- `spirv-unknown-spv1.2`
|
||||
- `spirv-unknown-spv1.3`
|
||||
- `spirv-unknown-spv1.4`
|
||||
- `spirv-unknown-spv1.5`
|
||||
|
||||
### Vulkan Targets
|
||||
|
||||
- `spirv-unknown-vulkan1.0`
|
||||
- `spirv-unknown-vulkan1.1`
|
||||
- `spirv-unknown-vulkan1.1spv1.4`
|
||||
- `spirv-unknown-vulkan1.2`
|
||||
|
||||
### WebGPU Targets
|
||||
|
||||
- `spirv-unknown-webgpu0`
|
||||
|
||||
### OpenGL Targets
|
||||
|
||||
- `spirv-unknown-opengl4.0`
|
||||
- `spirv-unknown-opengl4.1`
|
||||
- `spirv-unknown-opengl4.2`
|
||||
- `spirv-unknown-opengl4.3`
|
||||
- `spirv-unknown-opengl4.5`
|
||||
|
||||
### OpenCL Targets
|
||||
|
||||
- `spirv-unknown-opencl1.2`
|
||||
- `spirv-unknown-opencl1.2embedded`
|
||||
- `spirv-unknown-opencl2.0`
|
||||
- `spirv-unknown-opencl2.0embedded`
|
||||
- `spirv-unknown-opencl2.1`
|
||||
- `spirv-unknown-opencl2.1embedded`
|
||||
- `spirv-unknown-opencl2.2`
|
||||
- `spirv-unknown-opencl2.2embedded`
|
||||
|
||||
## GPU
|
||||
|
||||
Currently we don't have specific generations of GPUs for support, as long they support Vulkan 1.1+ with the latest officially installed drivers it should be able build and run the examples. You can check your Vulkan version using the [`vulkaninfo`] command from the `vulkan-sdk`.
|
||||
|
@ -15,7 +15,7 @@ You can now test out and try building shaders with rust-gpu from the browser!
|
||||
and run shaders on the web.
|
||||
- [Shader Playground] A playground for building and checking the output of
|
||||
shader code similar to godbolt or play.rust-lang.org.
|
||||
|
||||
|
||||
[SHADERed]: https://shadered.org/template
|
||||
[shader playground]: http://shader-playground.timjones.io/9d744d5893beb6a8f129fda50ad4aeeb
|
||||
|
||||
@ -70,15 +70,14 @@ Now you should have a `librustc_codegen_spirv` dynamic library available in
|
||||
`target/release`. You'll need to keep this somewhere stable that you can
|
||||
reference from your shader project.
|
||||
|
||||
Now we need to add our `.cargo/config` file. This tells cargo to build for the
|
||||
`spirv-unknown-unknown` target, and provides a path to the codegen backend for
|
||||
that target. We have to also provide `-Zbuild-std` as the
|
||||
`spirv-unknown-unknown` sysroot is not currently available in the
|
||||
default installation.
|
||||
Now we need to add our `.cargo/config` file. This is a configuration file that
|
||||
tells cargo how to build for SPIR-V. You need provide the target you're
|
||||
compiling for (see [platform support](./platform-support.md)) and provide a path to your built `rustc_codegen_spirv` dynamic
|
||||
library. We have to also provide `-Zbuild-std`.
|
||||
|
||||
```toml
|
||||
[build]
|
||||
target = "spirv-unknown-unknown"
|
||||
target = "spirv-unknown-spv1.3"
|
||||
rustflags = [
|
||||
"-Zcodegen-backend=<path_to_librustc_codegen_spirv>",
|
||||
"-Zsymbol-mangling-version=v0"
|
||||
@ -89,7 +88,7 @@ build-std=["core"]
|
||||
build-std-features=["compiler-builtins-mem"]
|
||||
```
|
||||
|
||||
Now we can build our crate with cargo as normal.
|
||||
Now we can build our crate with cargo as normal.
|
||||
```bash
|
||||
cargo build
|
||||
```
|
||||
|
@ -1,9 +1,8 @@
|
||||
use spirv_builder::SpirvBuilder;
|
||||
|
||||
fn main() {
|
||||
let result = SpirvBuilder::new("../shaders/sky-shader")
|
||||
let result = SpirvBuilder::new("../shaders/sky-shader", "spirv-unknown-spv1.3")
|
||||
.print_metadata(false)
|
||||
.spirv_version(1, 0)
|
||||
.build_multimodule()
|
||||
.unwrap();
|
||||
println!("{:#?}", result);
|
||||
|
@ -171,10 +171,13 @@ pub fn main() {
|
||||
}
|
||||
|
||||
pub fn compile_shaders() -> Vec<SpvFile> {
|
||||
let spv_paths: Vec<PathBuf> = vec![SpirvBuilder::new("examples/shaders/sky-shader")
|
||||
.print_metadata(false)
|
||||
.build()
|
||||
.unwrap()];
|
||||
let spv_paths: Vec<PathBuf> =
|
||||
vec![
|
||||
SpirvBuilder::new("examples/shaders/sky-shader", "spirv-unknown-vulkan1.1")
|
||||
.print_metadata(false)
|
||||
.build()
|
||||
.unwrap(),
|
||||
];
|
||||
let mut spv_files = Vec::<SpvFile>::with_capacity(spv_paths.len());
|
||||
for path in spv_paths.iter() {
|
||||
spv_files.push(SpvFile {
|
||||
|
@ -2,9 +2,7 @@ use spirv_builder::SpirvBuilder;
|
||||
use std::error::Error;
|
||||
|
||||
fn build_shader(path_to_create: &str) -> Result<(), Box<dyn Error>> {
|
||||
SpirvBuilder::new(path_to_create)
|
||||
.spirv_version(1, 0)
|
||||
.build()?;
|
||||
SpirvBuilder::new(path_to_create, "spirv-unknown-vulkan1.0").build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ impl Opt {
|
||||
pub fn environments(&self) -> Vec<String> {
|
||||
match &self.target_env {
|
||||
Some(env) => env.split(',').map(String::from).collect(),
|
||||
None => vec!["unknown".into()],
|
||||
None => vec!["spv1.3".into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user