384: Add include_spirv macro r=cwfitzgerald,rukai a=kvark

Depends on https://github.com/gfx-rs/wgpu/pull/729
Makes out shader loading in the examples a bit tidier.

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot]
2020-06-18 05:05:41 +00:00
committed by GitHub
16 changed files with 107 additions and 168 deletions

View File

@@ -28,14 +28,14 @@ vulkan = ["wgc/gfx-backend-vulkan"]
package = "wgpu-core"
version = "0.5"
git = "https://github.com/gfx-rs/wgpu"
rev = "fc2dd481b2713cd0eda6ffa540faeaf7418fd051"
rev = "6bca38feed155aa80776ae5e7dfa4769c655b638"
features = ["raw-window-handle"]
[dependencies.wgt]
package = "wgpu-types"
version = "0.5"
git = "https://github.com/gfx-rs/wgpu"
rev = "fc2dd481b2713cd0eda6ffa540faeaf7418fd051"
rev = "6bca38feed155aa80776ae5e7dfa4769c655b638"
[dependencies]
arrayvec = "0.5"

View File

@@ -6,8 +6,6 @@ extern crate rand;
#[path = "../framework.rs"]
mod framework;
use wgpu::vertex_attr_array;
// number of boid particles to simulate
const NUM_PARTICLES: u32 = 1500;
@@ -36,17 +34,9 @@ impl framework::Example for Example {
) -> (Self, Option<wgpu::CommandBuffer>) {
// load (and compile) shaders and create shader modules
let boids = include_bytes!("boids.comp.spv");
let boids_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&boids[..])).unwrap());
let vs = include_bytes!("shader.vert.spv");
let vs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs[..])).unwrap());
let fs = include_bytes!("shader.frag.spv");
let fs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs[..])).unwrap());
let boids_module = device.create_shader_module(wgpu::include_spirv!("boids.comp.spv"));
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
// buffer for simulation parameters uniform
@@ -148,12 +138,12 @@ impl framework::Example for Example {
wgpu::VertexBufferDescriptor {
stride: 4 * 4,
step_mode: wgpu::InputStepMode::Instance,
attributes: &vertex_attr_array![0 => Float2, 1 => Float2],
attributes: &wgpu::vertex_attr_array![0 => Float2, 1 => Float2],
},
wgpu::VertexBufferDescriptor {
stride: 2 * 4,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &vertex_attr_array![2 => Float2],
attributes: &wgpu::vertex_attr_array![2 => Float2],
},
],
},

View File

@@ -234,12 +234,8 @@ impl framework::Example for Example {
});
// Create the render pipeline
let vs_bytes = include_bytes!("shader.vert.spv");
let fs_bytes = include_bytes!("shader.frag.spv");
let vs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap());
let fs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap());
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,

View File

@@ -47,9 +47,7 @@ async fn execute_gpu(numbers: Vec<u32>) -> Vec<u32> {
.await
.unwrap();
let cs = include_bytes!("shader.comp.spv");
let cs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&cs[..])).unwrap());
let cs_module = device.create_shader_module(wgpu::include_spirv!("shader.comp.spv"));
let staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,

View File

@@ -32,13 +32,8 @@ async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu::
.await
.unwrap();
let vs = include_bytes!("shader.vert.spv");
let vs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs[..])).unwrap());
let fs = include_bytes!("shader.frag.spv");
let fs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs[..])).unwrap());
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],

View File

@@ -3,8 +3,6 @@ mod framework;
use bytemuck::{Pod, Zeroable};
use wgpu::vertex_attr_array;
const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
#[repr(C)]
@@ -105,12 +103,8 @@ impl Example {
bind_group_layouts: &[&bind_group_layout],
});
let vs_bytes = include_bytes!("blit.vert.spv");
let fs_bytes = include_bytes!("blit.frag.spv");
let vs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap());
let fs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap());
let vs_module = device.create_shader_module(wgpu::include_spirv!("blit.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("blit.frag.spv"));
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,
@@ -337,12 +331,8 @@ impl framework::Example for Example {
});
// Create the render pipeline
let vs_bytes = include_bytes!("draw.vert.spv");
let fs_bytes = include_bytes!("draw.frag.spv");
let vs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap());
let fs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap());
let vs_module = device.create_shader_module(wgpu::include_spirv!("draw.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("draw.frag.spv"));
let draw_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,
@@ -374,7 +364,7 @@ impl framework::Example for Example {
vertex_buffers: &[wgpu::VertexBufferDescriptor {
stride: vertex_size as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &vertex_attr_array![0 => Float4],
attributes: &wgpu::vertex_attr_array![0 => Float4],
}],
},
sample_count: 1,

View File

@@ -14,8 +14,6 @@ use std::iter;
use bytemuck::{Pod, Zeroable};
use wgpu::vertex_attr_array;
#[repr(C)]
#[derive(Clone, Copy)]
struct Vertex {
@@ -81,7 +79,7 @@ impl Example {
vertex_buffers: &[wgpu::VertexBufferDescriptor {
stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &vertex_attr_array![0 => Float2, 1 => Float4],
attributes: &wgpu::vertex_attr_array![0 => Float2, 1 => Float4],
}],
},
sample_count,
@@ -138,12 +136,8 @@ impl framework::Example for Example {
log::info!("Press left/right arrow keys to change sample_count.");
let sample_count = 4;
let vs_bytes = include_bytes!("shader.vert.spv");
let fs_bytes = include_bytes!("shader.frag.spv");
let vs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap());
let fs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap());
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],

View File

@@ -5,8 +5,6 @@ mod framework;
use bytemuck::{Pod, Zeroable};
use wgpu::vertex_attr_array;
#[repr(C)]
#[derive(Clone, Copy)]
@@ -426,7 +424,7 @@ impl framework::Example for Example {
let vb_desc = wgpu::VertexBufferDescriptor {
stride: vertex_size as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &vertex_attr_array![0 => Char4, 1 => Char4],
attributes: &wgpu::vertex_attr_array![0 => Char4, 1 => Char4],
};
let shadow_pass = {
@@ -466,14 +464,8 @@ impl framework::Example for Example {
});
// Create the render pipeline
let vs_bytes = include_bytes!("bake.vert.spv");
let fs_bytes = include_bytes!("bake.frag.spv");
let vs_module = device.create_shader_module(
&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap(),
);
let fs_module = device.create_shader_module(
&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap(),
);
let vs_module = device.create_shader_module(wgpu::include_spirv!("bake.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("bake.frag.spv"));
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,
@@ -602,14 +594,8 @@ impl framework::Example for Example {
});
// Create the render pipeline
let vs_bytes = include_bytes!("forward.vert.spv");
let fs_bytes = include_bytes!("forward.frag.spv");
let vs_module = device.create_shader_module(
&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap(),
);
let fs_module = device.create_shader_module(
&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap(),
);
let vs_module = device.create_shader_module(wgpu::include_spirv!("forward.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("forward.frag.spv"));
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,

View File

@@ -69,12 +69,8 @@ impl framework::Example for Skybox {
});
// Create the render pipeline
let vs_bytes = include_bytes!("shader.vert.spv");
let fs_bytes = include_bytes!("shader.frag.spv");
let vs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap());
let fs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap());
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let aspect = sc_desc.width as f32 / sc_desc.height as f32;
let uniforms = Self::generate_uniforms(aspect);

View File

@@ -97,29 +97,26 @@ impl framework::Example for Example {
queue: &wgpu::Queue,
) -> (Self, Option<wgpu::CommandBuffer>) {
let mut uniform_workaround = false;
let vs_bytes: &[u8] = include_bytes!("shader.vert.spv");
let fs_bytes: &[u8] = match device.capabilities() {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_bytes: Vec<u8> = match device.capabilities() {
c if c.contains(wgpu::Capabilities::UNSIZED_BINDING_ARRAY) => {
include_bytes!("unsized-non-uniform.frag.spv")
include_bytes!("unsized-non-uniform.frag.spv").to_vec()
}
c if c.contains(wgpu::Capabilities::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING) => {
include_bytes!("non-uniform.frag.spv")
include_bytes!("non-uniform.frag.spv").to_vec()
}
c if c.contains(wgpu::Capabilities::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING) => {
uniform_workaround = true;
include_bytes!("uniform.frag.spv")
include_bytes!("uniform.frag.spv").to_vec()
}
c if c.contains(wgpu::Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY) => {
include_bytes!("constant.frag.spv")
include_bytes!("constant.frag.spv").to_vec()
}
_ => {
panic!("Graphics adapter does not support any of the capabilities needed for this example");
}
};
let vs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(vs_bytes)).unwrap());
let fs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(fs_bytes)).unwrap());
let fs_module = device.create_shader_module(wgpu::util::make_spirv(&fs_bytes));
let vertex_size = std::mem::size_of::<Vertex>();
let vertex_data = create_vertices();

View File

@@ -4,8 +4,7 @@ mod framework;
mod point_gen;
use cgmath::Point3;
use std::{io, mem};
use wgpu::vertex_attr_array;
use std::mem;
///
/// Radius of the terrain.
@@ -477,23 +476,15 @@ impl framework::Example for Example {
label: Some("Terrain Flipped Bind Group"),
});
// Read shaders from file.
let water_vs_bytes = include_bytes!("water_shader.vert.spv");
let water_fs_bytes = include_bytes!("water_shader.frag.spv");
// Upload/compile them to GPU code.
let water_vs_module = device
.create_shader_module(&wgpu::read_spirv(io::Cursor::new(&water_vs_bytes[..])).unwrap());
let water_fs_module = device
.create_shader_module(&wgpu::read_spirv(io::Cursor::new(&water_fs_bytes[..])).unwrap());
let terrain_vs_bytes = include_bytes!("terrain_shader.vert.spv");
let terrain_fs_bytes = include_bytes!("terrain_shader.frag.spv");
let terrain_vs_module = device.create_shader_module(
&wgpu::read_spirv(io::Cursor::new(&terrain_vs_bytes[..])).unwrap(),
);
let terrain_fs_module = device.create_shader_module(
&wgpu::read_spirv(io::Cursor::new(&terrain_fs_bytes[..])).unwrap(),
);
let water_vs_module =
device.create_shader_module(wgpu::include_spirv!("water_shader.vert.spv"));
let water_fs_module =
device.create_shader_module(wgpu::include_spirv!("water_shader.frag.spv"));
let terrain_vs_module =
device.create_shader_module(wgpu::include_spirv!("terrain_shader.vert.spv"));
let terrain_fs_module =
device.create_shader_module(wgpu::include_spirv!("terrain_shader.frag.spv"));
// Create the render pipelines. These describe how the data will flow through the GPU, and what
// constraints and modifiers it will have.
@@ -566,7 +557,7 @@ impl framework::Example for Example {
vertex_buffers: &[wgpu::VertexBufferDescriptor {
stride: water_vertex_size as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &vertex_attr_array![0 => Short2, 1 => Char4],
attributes: &wgpu::vertex_attr_array![0 => Short2, 1 => Char4],
}],
},
sample_count: 1,
@@ -613,7 +604,7 @@ impl framework::Example for Example {
vertex_buffers: &[wgpu::VertexBufferDescriptor {
stride: terrain_vertex_size as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &vertex_attr_array![0 => Float3, 1 => Float3, 2 => Uchar4Norm],
attributes: &wgpu::vertex_attr_array![0 => Float3, 1 => Float3, 2 => Uchar4Norm],
}],
},
sample_count: 1,

View File

@@ -2,7 +2,8 @@ use crate::{
backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource,
BufferDescriptor, Capabilities, CommandEncoderDescriptor, ComputePipelineDescriptor,
Extensions, Limits, MapMode, PipelineLayoutDescriptor, RenderPipelineDescriptor,
SamplerDescriptor, SwapChainStatus, TextureDescriptor, TextureViewDescriptor,
SamplerDescriptor, ShaderModuleSource, SwapChainStatus, TextureDescriptor,
TextureViewDescriptor,
};
use arrayvec::ArrayVec;
@@ -402,15 +403,13 @@ impl crate::Context for Context {
fn device_create_shader_module(
&self,
device: &Self::DeviceId,
spv: &[u32],
source: ShaderModuleSource,
) -> Self::ShaderModuleId {
let desc = wgc::pipeline::ShaderModuleDescriptor {
code: wgc::U32Array {
bytes: spv.as_ptr(),
length: spv.len(),
},
let desc = match source {
ShaderModuleSource::SpirV(spv) => wgc::pipeline::ShaderModuleSource::SpirV(spv),
ShaderModuleSource::Wgsl(code) => wgc::pipeline::ShaderModuleSource::Wgsl(code),
};
gfx_select!(*device => self.device_create_shader_module(*device, &desc, PhantomData))
gfx_select!(*device => self.device_create_shader_module(*device, desc, PhantomData))
}
fn device_create_bind_group_layout(

View File

@@ -1,8 +1,8 @@
use crate::{
BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor,
CommandEncoderDescriptor, ComputePipelineDescriptor, PipelineLayoutDescriptor,
ProgrammableStageDescriptor, RenderPipelineDescriptor, SamplerDescriptor, SwapChainStatus,
TextureDescriptor, TextureViewDescriptor, TextureViewDimension,
ProgrammableStageDescriptor, RenderPipelineDescriptor, SamplerDescriptor, ShaderModuleSource,
SwapChainStatus, TextureDescriptor, TextureViewDescriptor, TextureViewDimension,
};
use futures::FutureExt;
@@ -773,9 +773,16 @@ impl crate::Context for Context {
fn device_create_shader_module(
&self,
device: &Self::DeviceId,
spv: &[u32],
source: ShaderModuleSource,
) -> Self::ShaderModuleId {
let desc = web_sys::GpuShaderModuleDescriptor::new(&js_sys::Uint32Array::from(spv));
let desc = match source {
ShaderModuleSource::SpirV(spv) => {
web_sys::GpuShaderModuleDescriptor::new(&js_sys::Uint32Array::from(spv))
}
ShaderModuleSource::Wgsl(code) => {
panic!("WGSL is not yet supported by the Web backend")
}
};
// TODO: label
Sendable(device.0.create_shader_module(&desc))
}

View File

@@ -1,7 +1,7 @@
//! A cross-platform graphics and compute library based on WebGPU.
mod backend;
mod util;
pub mod util;
#[macro_use]
mod macros;
@@ -33,7 +33,6 @@ pub use wgt::{
};
use backend::Context as C;
pub use util::read_spirv;
trait ComputePassInner<Ctx: Context> {
fn set_pipeline(&mut self, pipeline: &Ctx::ComputePipelineId);
@@ -162,7 +161,7 @@ trait Context: Sized {
fn device_create_shader_module(
&self,
device: &Self::DeviceId,
spv: &[u32],
source: ShaderModuleSource,
) -> Self::ShaderModuleId;
fn device_create_bind_group_layout(
&self,
@@ -571,6 +570,20 @@ impl Drop for ShaderModule {
}
}
/// Source of a shader module.
pub enum ShaderModuleSource<'a> {
/// SPIR-V module represented as a slice of words.
/// wgpu-rs will try to reflect it and use for validation, but the
/// original data is passed to gfx-rs and spirv_cross for translation.
SpirV(&'a [u32]),
/// WGSL module as a string slice.
/// wgpu-rs will parse it and use for validation. It will attempt
/// to build a SPIR-V module internally and panic otherwise.
///
/// Note: WGSL is not yet supported on the Web.
Wgsl(&'a str),
}
/// An opaque handle to a pipeline layout.
///
/// A `PipelineLayout` object describes the available binding groups of a pipeline.
@@ -1046,11 +1059,11 @@ impl Device {
Context::device_capabilities(&*self.context, &self.id)
}
/// Creates a shader module from SPIR-V source code.
pub fn create_shader_module(&self, spv: &[u32]) -> ShaderModule {
/// Creates a shader module from either SPIR-V or WGSL source code.
pub fn create_shader_module(&self, source: ShaderModuleSource) -> ShaderModule {
ShaderModule {
context: Arc::clone(&self.context),
id: Context::device_create_shader_module(&*self.context, &self.id, spv),
id: Context::device_create_shader_module(&*self.context, &self.id, source),
}
}

View File

@@ -135,3 +135,13 @@ fn test_vertex_attr_array() {
assert_eq!(attrs[1].offset, std::mem::size_of::<(f32, f32)>() as u64);
assert_eq!(attrs[1].shader_location, 3);
}
/// Macro to load a SPIR-V module statically.
///
/// It ensure the word alignment as well as the magic number.
#[macro_export]
macro_rules! include_spirv {
($path:literal) => {
$crate::util::make_spirv(&$crate::util::WordAligned(*include_bytes!($path)).0)
};
}

View File

@@ -1,41 +1,18 @@
use std::{io, slice};
#[repr(align(4))]
pub struct WordAligned<Bytes: ?Sized>(pub Bytes);
// TODO: This is copy/pasted from gfx-hal, so we need to find a new place to put
// this function
pub fn read_spirv<R: io::Read + io::Seek>(mut x: R) -> io::Result<Vec<u32>> {
let size = x.seek(io::SeekFrom::End(0))?;
if size % 4 != 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"input length not divisible by 4",
));
}
if size > usize::max_value() as u64 {
return Err(io::Error::new(io::ErrorKind::InvalidData, "input too long"));
}
let words = (size / 4) as usize;
let mut result = Vec::<u32>::with_capacity(words);
x.seek(io::SeekFrom::Start(0))?;
unsafe {
// Writing all bytes through a pointer with less strict alignment when our type has no
// invalid bitpatterns is safe.
x.read_exact(slice::from_raw_parts_mut(
result.as_mut_ptr() as *mut u8,
words * 4,
))?;
result.set_len(words);
}
/// Treat the given by slice as a SPIR-V module.
/// The pointer has to be aligned to 32-bit boundary and be a valid SPIR-V binary.
pub fn make_spirv<'a>(data: &'a [u8]) -> super::ShaderModuleSource<'a> {
const MAGIC_NUMBER: u32 = 0x0723_0203;
if !result.is_empty() && result[0] == MAGIC_NUMBER.swap_bytes() {
for word in &mut result {
*word = word.swap_bytes();
}
}
if result.is_empty() || result[0] != MAGIC_NUMBER {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"input missing SPIR-V magic number",
));
}
Ok(result)
let (pre, words, post) = unsafe { data.align_to::<u32>() };
assert_eq!(pre, &[], "data offset is not aligned to words!");
assert_eq!(post, &[], "data size is not aligned to words!");
assert_eq!(
words[0], MAGIC_NUMBER,
"wrong magic word {:x}. Make sure you are using a binary SPIRV file.",
words[0]
);
super::ShaderModuleSource::SpirV(words)
}