Browse Source

use heuristics to determine SPIR-V files and their entry points

master
Alison Watson 11 months ago
parent
commit
228ef7cffc
  1. 4
      Cargo.lock
  2. 3
      bl/main.rs
  3. 44
      fw/data/read.rs
  4. 58
      fw/data/shader.rs
  5. 7
      fw/render.rs
  6. 8
      fw/render/shader.rs

4
Cargo.lock generated

@ -277,9 +277,9 @@ checksum = "71cc4b8f7ec707459fdeddb4f137109947045592f5b0c139f7bf1360058bac6b"
[[package]]
name = "syn"
version = "1.0.76"
version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
dependencies = [
"proc-macro2",
"quote",

3
bl/main.rs

@ -12,8 +12,7 @@ fn entry(conf: &fw::conf::Conf) -> Result<(), Box<dyn std::error::Error>> {
&win,
&conf.render,
&[fw::data::shader::Module::compile(
vfs.get("main.wgsl").unwrap().text()?,
"main",
vfs.get("main.wgsl").unwrap().data(),
)?],
)?;

44
fw/data/read.rs

@ -13,6 +13,50 @@ pub const fn u8x(b: &[u8], p: usize) -> u8 {
b[p]
}
/// Reads an [`i16`] in big-endian order from a [`&[u8]`][slice].
pub const fn i16be(b: &[u8], p: usize) -> i16 {
i16::from_be_bytes([b[p], b[p + 1]])
}
/// Reads a [`u16`] in big-endian order from a [`&[u8]`][slice].
pub const fn u16be(b: &[u8], p: usize) -> u16 {
u16::from_be_bytes([b[p], b[p + 1]])
}
/// Reads an [`i32`] in big-endian order from a [`&[u8]`][slice].
pub const fn i32be(b: &[u8], p: usize) -> i32 {
i32::from_be_bytes([b[p], b[p + 1], b[p + 2], b[p + 3]])
}
/// Reads a [`u32`] in big-endian order from a [`&[u8]`][slice].
pub const fn u32be(b: &[u8], p: usize) -> u32 {
u32::from_be_bytes([b[p], b[p + 1], b[p + 2], b[p + 3]])
}
/// Reads an [`f16`] in big-endian order from a [`&[u8]`][slice].
pub fn f16be(b: &[u8], p: usize) -> f16 {
f16::from_be_bytes([b[p], b[p + 1]])
}
/// Reads an [`f32`] in big-endian order from a [`&[u8]`][slice].
pub fn f32be(b: &[u8], p: usize) -> f32 {
f32::from_be_bytes([b[p], b[p + 1], b[p + 2], b[p + 3]])
}
/// Reads an [`f64`] in big-endian order from a [`&[u8]`][slice].
pub fn f64be(b: &[u8], p: usize) -> f64 {
f64::from_be_bytes([
b[p],
b[p + 1],
b[p + 2],
b[p + 3],
b[p + 4],
b[p + 5],
b[p + 6],
b[p + 7],
])
}
/// Reads an [`i16`] in little-endian order from a [`&[u8]`][slice].
pub const fn i16le(b: &[u8], p: usize) -> i16 {
i16::from_le_bytes([b[p], b[p + 1]])

58
fw/data/shader.rs

@ -1,5 +1,7 @@
//! SPIR-V shaders.
use crate::types::Conv;
bitflags::bitflags! {
/// A flag set of shader stages.
pub struct ShaderStage: u8 {
@ -12,14 +14,14 @@ bitflags::bitflags! {
/// A SPIR-V IR module.
pub struct Module {
pub code: Vec<u32>,
pub size: usize,
pub name: String,
pub stag: ShaderStage,
}
/// The error type which is returned from reading a shader program.
#[derive(thiserror::Error, Debug)]
pub enum Err {
#[error(transparent)]
Text(#[from] std::str::Utf8Error),
#[error("{}", err.emit_to_string(src))]
Parse { err: naga::front::wgsl::ParseError, src: String },
#[error(transparent)]
@ -29,21 +31,59 @@ pub enum Err {
}
impl Module {
/// Reads a SPIR-V IR or WGSL source from a buffer.
pub fn compile(src: &[u8]) -> Result<Self, Err> {
if src[0..4] == [0x07, 0x23, 0x02, 0x03]
|| src[0..4] == [0x03, 0x02, 0x23, 0x07]
{
Self::read_spv(src)
} else {
Self::compile_wgsl(std::str::from_utf8(src)?)
}
}
/// Reads SPIR-V IR in from a buffer.
pub fn read(bytecode: &[u8], name: &str, stag: ShaderStage) -> Self {
fn read_spv(bytecode: &[u8]) -> Result<Self, Err> {
let read_word = if bytecode[0..4] == [0x07, 0x23, 0x02, 0x03] {
super::read::u32be
} else {
super::read::u32le
};
let mut code = Vec::with_capacity(bytecode.len() / 4);
for word in bytecode.chunks_exact(4) {
code.push(super::read::u32le(word, 0));
code.push(read_word(word, 0));
}
let size = code.len() * 4;
let mut stag = ShaderStage::empty();
let mut it = code.iter().peekable();
it.nth(4);
while let Some(w) = it.next() {
match w & 0xFFFF {
| 15 => {
if let Some(model) = it.peek() {
stag |= match model {
| 0 => ShaderStage::VERTEX,
| 4 => ShaderStage::FRAGMENT,
| 5 => ShaderStage::COMPUTE,
| _ => ShaderStage::empty(),
};
}
}
| _ => {}
}
let n = w & 0xFFFF_0000 >> 16;
if n > 0 {
it.nth(usize::conv(n) - 1);
}
}
Self { code, size, name: name.to_owned(), stag }
Ok(Self { code, stag })
}
/// Compiles WGSL source to SPIR-V IR.
pub fn compile(source: &str, name: &str) -> Result<Self, Err> {
fn compile_wgsl(source: &str) -> Result<Self, Err> {
use naga::back::spv;
let modu = naga::front::wgsl::parse_str(source)
@ -79,9 +119,7 @@ impl Module {
let code = spv::write_vec(&modu, &info, &opts)?;
let size = code.len() * 4;
Ok(Self { code, size, name: name.to_owned(), stag })
Ok(Self { code, stag })
}
}

7
fw/render.rs

@ -1,4 +1,3 @@
#![allow(unused_variables)]
#![allow(dead_code)]
// TODO: remove allow
@ -22,10 +21,7 @@ use ash::{
version::{DeviceV1_0, EntryV1_0, InstanceV1_0},
vk,
};
use std::{
ffi::{CStr, CString},
mem::size_of,
};
use std::{ffi::CStr, mem::size_of};
struct StaticData {
pipe_layout: vk::PipelineLayout,
@ -106,7 +102,6 @@ struct Buf {
struct Shader {
module: vk::ShaderModule,
name: CString,
stages: Vec<vk::ShaderStageFlags>,
}

8
fw/render/shader.rs

@ -19,17 +19,15 @@ impl Shader {
pub(super) unsafe fn new(
dev: &ash::Device, shader: &data::shader::Module,
) -> Result<Self, Err> {
let name = CString::new(shader.name.as_str())?;
let create_info = vk::ShaderModuleCreateInfo {
code_size: shader.size,
code_size: shader.code.len() * 4,
p_code: shader.code.as_ptr(),
..Default::default()
};
let module = unsafe { dev.create_shader_module(&create_info, None)? };
Ok(Self { module, name, stages: read_stages(shader.stag) })
Ok(Self { module, stages: read_stages(shader.stag) })
}
pub(super) unsafe fn destroy(&self, dev: &ash::Device) {
@ -42,7 +40,7 @@ impl Shader {
vec_e![&self.stages, |&stage| vk::PipelineShaderStageCreateInfo {
stage,
module: self.module,
p_name: self.name.as_ptr(),
p_name: c_str!("main"),
..Default::default()
}]
}

Loading…
Cancel
Save