mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Implement basic SPIR-V back-end (#46)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.DS_Store
|
||||
.idea
|
||||
|
||||
@@ -18,7 +18,7 @@ Rust | | |
|
||||
|
||||
Back-end | Status | Notes |
|
||||
--------------- | ------------------ | ----- |
|
||||
SPIR-V (binary) | | |
|
||||
SPIR-V (binary) | :construction: | |
|
||||
WGSL | | |
|
||||
Metal | :construction: | |
|
||||
HLSL | | |
|
||||
|
||||
@@ -30,7 +30,7 @@ fn main() {
|
||||
return;
|
||||
} else if args[1].ends_with(".spv") {
|
||||
let input = fs::read(&args[1]).unwrap();
|
||||
naga::front::spirv::parse_u8_slice(&input).unwrap()
|
||||
naga::front::spv::parse_u8_slice(&input).unwrap()
|
||||
} else if args[1].ends_with(".wgsl") {
|
||||
let input = fs::read_to_string(&args[1]).unwrap();
|
||||
naga::front::wgsl::parse_str(&input).unwrap()
|
||||
@@ -71,6 +71,27 @@ fn main() {
|
||||
};
|
||||
let msl = msl::write_string(&module, options).unwrap();
|
||||
fs::write(&args[2], msl).unwrap();
|
||||
} else if args[2].ends_with(".spv") {
|
||||
use naga::back::spv;
|
||||
|
||||
let debug_flag = args.get(3).map_or(spv::WriterFlags::DEBUG, |arg| {
|
||||
if arg.parse().unwrap() {
|
||||
spv::WriterFlags::DEBUG
|
||||
} else {
|
||||
spv::WriterFlags::NONE
|
||||
}
|
||||
});
|
||||
|
||||
let spv = spv::writer::Writer::new(&module.header, debug_flag).write(&module);
|
||||
|
||||
let bytes = spv
|
||||
.iter()
|
||||
.fold(Vec::with_capacity(spv.len() * 4), |mut v, w| {
|
||||
v.extend_from_slice(&w.to_le_bytes());
|
||||
v
|
||||
});
|
||||
|
||||
fs::write(&args[2], bytes.as_slice()).unwrap();
|
||||
} else {
|
||||
panic!("Unknown output: {:?}", args[2]);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{fmt, hash, marker::PhantomData, num::NonZeroU32};
|
||||
|
||||
/// An unique index in the arena array that a handle points to.
|
||||
///
|
||||
/// This type is independent of `spirv::Word`. `spirv::Word` is used in data
|
||||
/// This type is independent of `spv::Word`. `spv::Word` is used in data
|
||||
/// representation. It holds a SPIR-V and refers to that instruction. In
|
||||
/// structured representation, we use Handle to refer to an SPIR-V instruction.
|
||||
/// `Index` is an implementation detail to `Handle`.
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod msl;
|
||||
pub mod spv;
|
||||
|
||||
85
src/back/spv/layout.rs
Normal file
85
src/back/spv/layout.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use crate::back::spv::{Instruction, LogicalLayout, PhysicalLayout};
|
||||
use spirv::*;
|
||||
use std::iter;
|
||||
|
||||
impl PhysicalLayout {
|
||||
pub(crate) fn new(header: &crate::Header) -> Self {
|
||||
let version: Word = ((header.version.0 as u32) << 16)
|
||||
| ((header.version.1 as u32) << 8)
|
||||
| header.version.2 as u32;
|
||||
|
||||
PhysicalLayout {
|
||||
magic_number: MAGIC_NUMBER,
|
||||
version,
|
||||
generator: header.generator,
|
||||
bound: 0,
|
||||
instruction_schema: 0x0u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn in_words(&self, sink: &mut impl Extend<Word>) {
|
||||
sink.extend(iter::once(self.magic_number));
|
||||
sink.extend(iter::once(self.version));
|
||||
sink.extend(iter::once(self.generator));
|
||||
sink.extend(iter::once(self.bound));
|
||||
sink.extend(iter::once(self.instruction_schema));
|
||||
}
|
||||
}
|
||||
|
||||
impl LogicalLayout {
|
||||
pub(crate) fn in_words(&self, sink: &mut impl Extend<Word>) {
|
||||
sink.extend(self.capabilities.iter().cloned());
|
||||
sink.extend(self.extensions.iter().cloned());
|
||||
sink.extend(self.ext_inst_imports.iter().cloned());
|
||||
sink.extend(self.memory_model.iter().cloned());
|
||||
sink.extend(self.entry_points.iter().cloned());
|
||||
sink.extend(self.execution_modes.iter().cloned());
|
||||
sink.extend(self.debugs.iter().cloned());
|
||||
sink.extend(self.annotations.iter().cloned());
|
||||
sink.extend(self.declarations.iter().cloned());
|
||||
sink.extend(self.function_declarations.iter().cloned());
|
||||
sink.extend(self.function_definitions.iter().cloned());
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub(crate) fn new(op: Op) -> Self {
|
||||
Instruction {
|
||||
op,
|
||||
wc: 1, // Always start at 1 for the first word (OP + WC),
|
||||
type_id: None,
|
||||
result_id: None,
|
||||
operands: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_type(&mut self, id: Word) {
|
||||
assert!(self.type_id.is_none(), "Type can only be set once");
|
||||
self.type_id = Some(id);
|
||||
self.wc += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn set_result(&mut self, id: Word) {
|
||||
assert!(self.result_id.is_none(), "Result can only be set once");
|
||||
self.result_id = Some(id);
|
||||
self.wc += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn add_operand(&mut self, operand: Word) {
|
||||
self.operands.push(operand);
|
||||
self.wc += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn add_operands(&mut self, operands: Vec<Word>) {
|
||||
for operand in operands.into_iter() {
|
||||
self.add_operand(operand)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_words(&self, sink: &mut impl Extend<Word>) {
|
||||
sink.extend(Some((self.wc << 16 | self.op as u32) as u32));
|
||||
sink.extend(self.type_id);
|
||||
sink.extend(self.result_id);
|
||||
sink.extend(self.operands.iter().cloned());
|
||||
}
|
||||
}
|
||||
42
src/back/spv/mod.rs
Normal file
42
src/back/spv/mod.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
mod layout;
|
||||
pub mod writer;
|
||||
|
||||
use spirv::*;
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct WriterFlags: u32 {
|
||||
const NONE = 0x0;
|
||||
const DEBUG = 0x1;
|
||||
}
|
||||
}
|
||||
|
||||
struct PhysicalLayout {
|
||||
magic_number: Word,
|
||||
version: Word,
|
||||
generator: Word,
|
||||
bound: Word,
|
||||
instruction_schema: Word,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct LogicalLayout {
|
||||
capabilities: Vec<Word>,
|
||||
extensions: Vec<Word>,
|
||||
ext_inst_imports: Vec<Word>,
|
||||
memory_model: Vec<Word>,
|
||||
entry_points: Vec<Word>,
|
||||
execution_modes: Vec<Word>,
|
||||
debugs: Vec<Word>,
|
||||
annotations: Vec<Word>,
|
||||
declarations: Vec<Word>,
|
||||
function_declarations: Vec<Word>,
|
||||
function_definitions: Vec<Word>,
|
||||
}
|
||||
|
||||
struct Instruction {
|
||||
op: Op,
|
||||
wc: u32,
|
||||
type_id: Option<Word>,
|
||||
result_id: Option<Word>,
|
||||
operands: Vec<Word>,
|
||||
}
|
||||
1013
src/back/spv/writer.rs
Normal file
1013
src/back/spv/writer.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
#[cfg(feature = "glsl")]
|
||||
pub mod glsl;
|
||||
pub mod spirv;
|
||||
pub mod spv;
|
||||
pub mod wgsl;
|
||||
|
||||
use crate::arena::Arena;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Our IR links to everything with `Handle`, while SPIR-V uses IDs.
|
||||
In order to keep track of the associations, the parser has many lookup tables.
|
||||
There map `spirv::Word` into a specific IR handle, plus potentially a bit of
|
||||
There map `spv::Word` into a specific IR handle, plus potentially a bit of
|
||||
extra info, such as the related SPIR-V type ID.
|
||||
TODO: would be nice to find ways that avoid looking up as much
|
||||
|
||||
Reference in New Issue
Block a user