vulkano-shaders cleanup (#1058)

Use syn to construct ast instead of raw strings
Move spirv searching methods from lib.rs into its own module
Improve formatting
This commit is contained in:
Lucas Kent 2018-10-05 17:00:02 +10:00 committed by GitHub
parent b6c4d84e61
commit d779829cbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1051 additions and 1125 deletions

View File

@ -1,5 +1,6 @@
# Unreleased (Breaking) # Unreleased (Breaking)
- `vulkano_shaders::reflect` now returns `Result<proc_macro2::TokenStream, Error>` instead of `Result<String, Error>`
- Removed mir support, as it is being removed from the vulkan spec. - Removed mir support, as it is being removed from the vulkan spec.
- Remove vulkano_shaders::build_glsl_shaders - Remove vulkano_shaders::build_glsl_shaders
- Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage` - Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage`

View File

@ -153,7 +153,7 @@ void main() {
// We need to signal a fence here because below we want to block the CPU until the GPU has // We need to signal a fence here because below we want to block the CPU until the GPU has
// reached that point in the execution. // reached that point in the execution.
.then_signal_fence_and_flush().unwrap(); .then_signal_fence_and_flush().unwrap();
// Blocks execution until the GPU has finished the operation. This method only exists on the // Blocks execution until the GPU has finished the operation. This method only exists on the
// future that corresponds to a signalled fence. In other words, this method wouldn't be // future that corresponds to a signalled fence. In other words, this method wouldn't be
// available if we didn't call `.then_signal_fence_and_flush()` earlier. // available if we didn't call `.then_signal_fence_and_flush()` earlier.

View File

@ -0,0 +1,102 @@
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
// TODO: Give a paragraph about what specialization are and what problems they solve
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::command_buffer::AutoCommandBufferBuilder;
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Device;
use vulkano::device::DeviceExtensions;
use vulkano::instance::Instance;
use vulkano::instance::InstanceExtensions;
use vulkano::pipeline::ComputePipeline;
use vulkano::sync::now;
use vulkano::sync::GpuFuture;
use std::sync::Arc;
fn main() {
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance).next().unwrap();
let queue_family = physical.queue_families().find(|&q| q.supports_compute()).unwrap();
let (device, mut queues) = {
Device::new(physical, physical.supported_features(), &DeviceExtensions::none(),
[(queue_family, 0.5)].iter().cloned()).expect("failed to create device")
};
let queue = queues.next().unwrap();
mod cs {
#[derive(VulkanoShader)]
#[ty = "compute"]
#[src = "
#version 450
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
layout(constant_id = 0) const int multiple = 64;
layout(constant_id = 1) const float addend = 64;
layout(constant_id = 2) const bool enable = true;
const vec2 foo = vec2(0, 0); // TODO: How do I hit Instruction::SpecConstantComposite
layout(set = 0, binding = 0) buffer Data {
uint data[];
} data;
void main() {
uint idx = gl_GlobalInvocationID.x;
if (enable) {
data.data[idx] *= multiple;
data.data[idx] += uint(addend);
}
}"
]
#[allow(dead_code)]
struct Dummy;
}
let shader = cs::Shader::load(device.clone())
.expect("failed to create shader module");
let spec_consts = cs::SpecializationConstants {
enable: 1,
multiple: 1,
addend: 1.0,
};
let pipeline = Arc::new(ComputePipeline::new(device.clone(), &shader.main_entry_point(), &spec_consts).unwrap());
let data_buffer = {
let data_iter = (0 .. 65536u32).map(|n| n);
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(),
data_iter).expect("failed to create buffer")
};
let set = Arc::new(PersistentDescriptorSet::start(pipeline.clone(), 0)
.add_buffer(data_buffer.clone()).unwrap()
.build().unwrap()
);
let command_buffer = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap()
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), ()).unwrap()
.build().unwrap();
let future = now(device.clone())
.then_execute(queue.clone(), command_buffer).unwrap()
.then_signal_fence_and_flush().unwrap();
future.wait(None).unwrap();
let data_buffer_content = data_buffer.read().expect("failed to lock buffer for reading");
for n in 0 .. 65536u32 {
assert_eq!(data_buffer_content[n as usize], n * 1 + 1);
}
println!("Success");
}

View File

@ -14,6 +14,8 @@ proc-macro = true
[dependencies] [dependencies]
syn = "0.15" syn = "0.15"
quote = "0.6"
proc-macro2 = "0.4"
vulkano-shaders = { version = "0.10", path = "../vulkano-shaders" } vulkano-shaders = { version = "0.10", path = "../vulkano-shaders" }
[dev-dependencies] [dev-dependencies]

View File

@ -156,6 +156,8 @@
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html //! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
extern crate proc_macro; extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn; extern crate syn;
extern crate vulkano_shaders; extern crate vulkano_shaders;
@ -164,15 +166,13 @@ use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::Path;
use proc_macro::TokenStream;
enum SourceKind { enum SourceKind {
Src(String), Src(String),
Path(String), Path(String),
} }
#[proc_macro_derive(VulkanoShader, attributes(src, path, ty))] #[proc_macro_derive(VulkanoShader, attributes(src, path, ty))]
pub fn derive(input: TokenStream) -> TokenStream { pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let syn_item: syn::DeriveInput = syn::parse(input).unwrap(); let syn_item: syn::DeriveInput = syn::parse(input).unwrap();
let source_code = { let source_code = {
@ -244,8 +244,5 @@ pub fn derive(input: TokenStream) -> TokenStream {
}; };
let content = vulkano_shaders::compile(&source_code, ty).unwrap(); let content = vulkano_shaders::compile(&source_code, ty).unwrap();
vulkano_shaders::reflect("Shader", content.as_binary()) vulkano_shaders::reflect("Shader", content.as_binary()).unwrap().into()
.unwrap()
.parse()
.unwrap()
} }

View File

@ -10,3 +10,6 @@ categories = ["rendering::graphics-api"]
[dependencies] [dependencies]
shaderc = "0.3" shaderc = "0.3"
syn = "0.15"
quote = "0.6"
proc-macro2 = "0.4"

View File

@ -1,45 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
extern crate vulkano_shaders;
fn main() {
let shader = r#"
#version 450
layout(constant_id = 5) const int index = 2;
struct S {
vec3 val1;
bool val2[5];
};
layout(set = 0, binding = 0) uniform sampler2D u_texture;
layout(set = 0, binding = 1) uniform Block {
S u_data;
} block;
layout(location = 0) in vec2 v_texcoords;
layout(location = 0) out vec4 f_color;
void main() {
if (block.u_data.val2[index]) {
f_color = texture(u_texture, v_texcoords);
} else {
f_color = vec4(1.0);
}
}
"#;
let content = vulkano_shaders::compile(shader, vulkano_shaders::ShaderKind::Fragment).unwrap();
let output = vulkano_shaders::reflect("Shader", content.as_binary()).unwrap();
println!("{}", output);
}

View File

@ -9,10 +9,13 @@
use std::cmp; use std::cmp;
use enums; use proc_macro2::TokenStream;
use parse;
pub fn write_descriptor_sets(doc: &parse::Spirv) -> String { use enums::{Dim, Decoration, StorageClass, ImageFormat};
use parse::{Instruction, Spirv};
use spirv_search;
pub fn write_descriptor_sets(doc: &Spirv) -> TokenStream {
// TODO: not implemented correctly // TODO: not implemented correctly
// Finding all the descriptors. // Finding all the descriptors.
@ -20,42 +23,35 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
struct Descriptor { struct Descriptor {
set: u32, set: u32,
binding: u32, binding: u32,
desc_ty: String, desc_ty: TokenStream,
array_count: u64, array_count: u64,
readonly: bool, readonly: bool,
} }
// Looping to find all the elements that have the `DescriptorSet` decoration. // Looping to find all the elements that have the `DescriptorSet` decoration.
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
let (variable_id, descriptor_set) = match instruction { let (variable_id, set) = match instruction {
&parse::Instruction::Decorate { &Instruction::Decorate { target_id, decoration: Decoration::DecorationDescriptorSet, ref params }
target_id, => (target_id, params[0]),
decoration: enums::Decoration::DecorationDescriptorSet,
ref params,
} => {
(target_id, params[0])
},
_ => continue, _ => continue,
}; };
// Find which type is pointed to by this variable. // Find which type is pointed to by this variable.
let pointed_ty = pointer_variable_ty(doc, variable_id); let pointed_ty = pointer_variable_ty(doc, variable_id);
// Name of the variable. // Name of the variable.
let name = ::name_from_id(doc, variable_id); let name = spirv_search::name_from_id(doc, variable_id);
// Find the binding point of this descriptor. // Find the binding point of this descriptor.
let binding = doc.instructions let binding = doc.instructions
.iter() .iter()
.filter_map(|i| { .filter_map(|i| {
match i { match i {
&parse::Instruction::Decorate { &Instruction::Decorate {
target_id, target_id,
decoration: enums::Decoration::DecorationBinding, decoration: Decoration::DecorationBinding,
ref params, ref params,
} if target_id == variable_id => { } if target_id == variable_id => Some(params[0]),
Some(params[0]) _ => None, // TODO: other types
},
_ => None, // TODO: other types
} }
}) })
.next() .next()
@ -63,31 +59,20 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
// Find information about the kind of binding for this descriptor. // Find information about the kind of binding for this descriptor.
let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false) let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
.expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe \ .expect(&format!(
unimplemented)", "Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)",
name, name,
pointed_ty)); pointed_ty
));
descriptors.push(Descriptor { descriptors.push(Descriptor { desc_ty, set, binding, array_count, readonly });
desc_ty: desc_ty,
set: descriptor_set,
binding: binding,
array_count: array_count,
readonly: readonly,
});
} }
// Looping to find all the push constant structs. // Looping to find all the push constant structs.
let mut push_constants_size = 0; let mut push_constants_size = 0;
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
let type_id = match instruction { let type_id = match instruction {
&parse::Instruction::TypePointer { &Instruction::TypePointer { type_id, storage_class: StorageClass::StorageClassPushConstant, .. }
type_id, => type_id,
storage_class: enums::StorageClass::StorageClassPushConstant,
..
} => {
type_id
},
_ => continue, _ => continue,
}; };
@ -100,127 +85,106 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
let descriptor_body = descriptors let descriptor_body = descriptors
.iter() .iter()
.map(|d| { .map(|d| {
format!( let set = d.set as usize;
"({set}, {binding}) => Some(DescriptorDesc {{ let binding = d.binding as usize;
ty: {desc_ty}, let desc_ty = &d.desc_ty;
array_count: {array_count}, let array_count = d.array_count as u32;
stages: self.0.clone(), let readonly = d.readonly;
readonly: {readonly}, quote!{
}}),", (#set, #binding) => Some(DescriptorDesc {
set = d.set, ty: #desc_ty,
binding = d.binding, array_count: #array_count,
desc_ty = d.desc_ty, stages: self.0.clone(),
array_count = d.array_count, readonly: #readonly,
readonly = if d.readonly { "true" } else { "false" } }),
) }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>();
.concat();
let num_sets = descriptors.iter().fold(0, |s, d| cmp::max(s, d.set + 1)); let num_sets = descriptors.iter().fold(0, |s, d| cmp::max(s, d.set + 1)) as usize;
// Writing the body of the `num_bindings_in_set` method. // Writing the body of the `num_bindings_in_set` method.
let num_bindings_in_set_body = { let num_bindings_in_set_body = (0 .. num_sets)
(0 .. num_sets) .map(|set| {
.map(|set| { let num = descriptors
let num = descriptors .iter()
.iter() .filter(|d| d.set == set as u32)
.filter(|d| d.set == set) .fold(0, |s, d| cmp::max(s, 1 + d.binding)) as usize;
.fold(0, |s, d| cmp::max(s, 1 + d.binding)); quote!{ #set => Some(#num), }
format!("{set} => Some({num}),", set = set, num = num) })
}) .collect::<Vec<_>>();
.collect::<Vec<_>>()
.concat()
};
// Writing the body of the `num_push_constants_ranges` method. // Writing the body of the `num_push_constants_ranges` method.
let num_push_constants_ranges_body = { let num_push_constants_ranges_body = if push_constants_size == 0 { 0 } else { 1 } as usize;
if push_constants_size == 0 { "0" } else { "1" }
};
// Writing the body of the `push_constants_range` method. // Writing the body of the `push_constants_range` method.
let push_constants_range_body = format!( let push_constants_range_body = quote!(
r#" if num != 0 || #push_constants_size == 0 {
if num != 0 || {pc_size} == 0 {{ return None; }} None
Some(PipelineLayoutDescPcRange {{ } else {
offset: 0, // FIXME: not necessarily true Some(PipelineLayoutDescPcRange {
size: {pc_size}, offset: 0, // FIXME: not necessarily true
stages: ShaderStages::all(), // FIXME: wrong size: #push_constants_size,
}}) stages: ShaderStages::all(), // FIXME: wrong
"#, })
pc_size = push_constants_size }
); );
format!( quote!{
r#"
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Layout(pub ShaderStages); pub struct Layout(pub ShaderStages);
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe impl PipelineLayoutDesc for Layout {{ unsafe impl PipelineLayoutDesc for Layout {
fn num_sets(&self) -> usize {{ fn num_sets(&self) -> usize {
{num_sets} #num_sets
}} }
fn num_bindings_in_set(&self, set: usize) -> Option<usize> {{ fn num_bindings_in_set(&self, set: usize) -> Option<usize> {
match set {{ match set {
{num_bindings_in_set_body} #( #num_bindings_in_set_body )*
_ => None _ => None
}} }
}} }
fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {{ fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {
match (set, binding) {{ match (set, binding) {
{descriptor_body} #( #descriptor_body )*
_ => None _ => None
}} }
}} }
fn num_push_constants_ranges(&self) -> usize {{ fn num_push_constants_ranges(&self) -> usize {
{num_push_constants_ranges_body} #num_push_constants_ranges_body
}} }
fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {{ fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {
{push_constants_range_body} #push_constants_range_body
}} }
}} }
"#, }
num_sets = num_sets,
num_bindings_in_set_body = num_bindings_in_set_body,
descriptor_body = descriptor_body,
num_push_constants_ranges_body = num_push_constants_ranges_body,
push_constants_range_body = push_constants_range_body
)
} }
/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed /// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type. /// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 { fn pointer_variable_ty(doc: &Spirv, variable: u32) -> u32 {
let var_ty = doc.instructions let var_ty = doc.instructions
.iter() .iter()
.filter_map(|i| match i { .filter_map(|i| match i {
&parse::Instruction::Variable { &Instruction::Variable { result_type_id, result_id, .. }
result_type_id, if result_id == variable => Some(result_type_id),
result_id, _ => None,
.. })
} if result_id == variable => {
Some(result_type_id)
},
_ => None,
})
.next() .next()
.unwrap(); .unwrap();
doc.instructions doc.instructions
.iter() .iter()
.filter_map(|i| match i { .filter_map(|i| match i {
&parse::Instruction::TypePointer { result_id, type_id, .. } &Instruction::TypePointer { result_id, type_id, .. }
if result_id == var_ty => { if result_id == var_ty => Some(type_id),
Some(type_id) _ => None,
}, })
_ => None,
})
.next() .next()
.unwrap() .unwrap()
} }
@ -229,26 +193,21 @@ fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
/// read-only, and the number of array elements. /// read-only, and the number of array elements.
/// ///
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface /// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sampled: bool) fn descriptor_infos(doc: &Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
-> Option<(String, bool, u64)> { -> Option<(TokenStream, bool, u64)>
{
doc.instructions.iter().filter_map(|i| { doc.instructions.iter().filter_map(|i| {
match i { match i {
&parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => { &Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
// Determine whether there's a Block or BufferBlock decoration. // Determine whether there's a Block or BufferBlock decoration.
let is_ssbo = doc.instructions.iter().filter_map(|i| { let is_ssbo = doc.instructions.iter().filter_map(|i| {
match i { match i {
&parse::Instruction::Decorate &Instruction::Decorate
{ target_id, decoration: enums::Decoration::DecorationBufferBlock, .. } { target_id, decoration: Decoration::DecorationBufferBlock, .. }
if target_id == pointed_ty => if target_id == pointed_ty => Some(true),
{ &Instruction::Decorate
Some(true) { target_id, decoration: Decoration::DecorationBlock, .. }
}, if target_id == pointed_ty => Some(false),
&parse::Instruction::Decorate
{ target_id, decoration: enums::Decoration::DecorationBlock, .. }
if target_id == pointed_ty =>
{
Some(false)
},
_ => None, _ => None,
} }
}).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \ }).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
@ -257,103 +216,111 @@ fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sa
// Determine whether there's a NonWritable decoration. // Determine whether there's a NonWritable decoration.
//let non_writable = false; // TODO: tricky because the decoration is on struct members //let non_writable = false; // TODO: tricky because the decoration is on struct members
let desc = format!("DescriptorDescTy::Buffer(DescriptorBufferDesc {{ let desc = quote!{
dynamic: Some(false), DescriptorDescTy::Buffer(DescriptorBufferDesc {
storage: {}, dynamic: Some(false),
}})", if is_ssbo { "true" } else { "false "}); storage: #is_ssbo,
})
};
Some((desc, true, 1)) Some((desc, true, 1))
}, }
&Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled, ref format, .. }
&parse::Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled, if result_id == pointed_ty =>
ref format, .. } if result_id == pointed_ty =>
{ {
let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \ let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
have a Sampled operand of 1 or 2"); have a Sampled operand of 1 or 2");
let ms = if ms { "true" } else { "false" }; let arrayed = match arrayed {
let arrayed = if arrayed { true => quote!{ DescriptorImageDescArray::Arrayed { max_layers: None } },
"DescriptorImageDescArray::Arrayed { max_layers: None }" false => quote!{ DescriptorImageDescArray::NonArrayed }
} else {
"DescriptorImageDescArray::NonArrayed"
}; };
if let &enums::Dim::DimSubpassData = dim { match dim {
// We are an input attachment. Dim::DimSubpassData => {
assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \ // We are an input attachment.
an OpTypeImage whose dimension is \ assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
SubpassData"); an OpTypeImage whose dimension is \
assert!(if let &enums::ImageFormat::ImageFormatUnknown = format { true } SubpassData");
else { false }, "If Dim is SubpassData, Image Format must be Unknown"); assert!(if let &ImageFormat::ImageFormatUnknown = format { true }
assert!(!sampled, "If Dim is SubpassData, Sampled must be 2"); else { false }, "If Dim is SubpassData, Image Format must be Unknown");
assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");
let desc = format!("DescriptorDescTy::InputAttachment {{ let desc = quote!{
multisampled: {}, DescriptorDescTy::InputAttachment {
array_layers: {} multisampled: #ms,
}}", ms, arrayed); array_layers: #arrayed
}
};
Some((desc, true, 1)) Some((desc, true, 1))
}
Dim::DimBuffer => {
// We are a texel buffer.
let not_sampled = !sampled;
let desc = quote!{
DescriptorDescTy::TexelBuffer {
storage: #not_sampled,
format: None, // TODO: specify format if known
}
};
} else if let &enums::Dim::DimBuffer = dim { Some((desc, true, 1))
// We are a texel buffer. }
let desc = format!("DescriptorDescTy::TexelBuffer {{ _ => {
storage: {}, // We are a sampled or storage image.
format: None, // TODO: specify format if known let ty = match force_combined_image_sampled {
}}", !sampled); true => quote!{ DescriptorDescTy::CombinedImageSampler },
false => quote!{ DescriptorDescTy::Image }
};
let dim = match *dim {
Dim::Dim1D => quote!{ DescriptorImageDescDimensions::OneDimensional },
Dim::Dim2D => quote!{ DescriptorImageDescDimensions::TwoDimensional },
Dim::Dim3D => quote!{ DescriptorImageDescDimensions::ThreeDimensional },
Dim::DimCube => quote!{ DescriptorImageDescDimensions::Cube },
Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
_ => unreachable!()
};
Some((desc, true, 1)) let desc = quote!{
#ty(DescriptorImageDesc {
sampled: #sampled,
dimensions: #dim,
format: None, // TODO: specify format if known
multisampled: #ms,
array_layers: #arrayed,
})
};
} else { Some((desc, true, 1))
// We are a sampled or storage image. }
let sampled = if sampled { "true" } else { "false" };
let ty = if force_combined_image_sampled { "CombinedImageSampler" }
else { "Image" };
let dim = match *dim {
enums::Dim::Dim1D => "DescriptorImageDescDimensions::OneDimensional",
enums::Dim::Dim2D => "DescriptorImageDescDimensions::TwoDimensional",
enums::Dim::Dim3D => "DescriptorImageDescDimensions::ThreeDimensional",
enums::Dim::DimCube => "DescriptorImageDescDimensions::Cube",
enums::Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
_ => unreachable!()
};
let desc = format!("DescriptorDescTy::{}(DescriptorImageDesc {{
sampled: {},
dimensions: {},
format: None, // TODO: specify format if known
multisampled: {},
array_layers: {},
}})", ty, sampled, dim, ms, arrayed);
Some((desc, true, 1))
} }
}, }
&parse::Instruction::TypeSampledImage { result_id, image_type_id } &Instruction::TypeSampledImage { result_id, image_type_id } if result_id == pointed_ty
if result_id == pointed_ty => => descriptor_infos(doc, image_type_id, true),
{
descriptor_infos(doc, image_type_id, true)
},
&parse::Instruction::TypeSampler { result_id } if result_id == pointed_ty => { &Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
let desc = format!("DescriptorDescTy::Sampler"); let desc = quote!{ DescriptorDescTy::Sampler };
Some((desc, true, 1)) Some((desc, true, 1))
}, }
&Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
&parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) { let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
None => return None, None => return None,
Some(v) => v, Some(v) => v,
}; };
assert_eq!(arr, 1); // TODO: implement? assert_eq!(arr, 1); // TODO: implement?
let len = doc.instructions.iter().filter_map(|e| { let len = doc.instructions.iter().filter_map(|e| {
match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None } match e {
&Instruction::Constant { result_id, ref data, .. }
if result_id == length_id => Some(data.clone()),
_ => None
}
}).next().expect("failed to find array length"); }).next().expect("failed to find array length");
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64); let len = len.iter().rev().fold(0, |a, &b| (a << 32) | b as u64);
Some((desc, readonly, len)) Some((desc, readonly, len))
}, }
_ => None, // TODO: other types
_ => None, // TODO: other types
} }
}).next() }).next()
} }

View File

@ -7,25 +7,17 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
use enums; use syn::Ident;
use parse; use proc_macro2::{Span, TokenStream};
use format_from_id; use enums::{StorageClass, ExecutionModel, ExecutionMode};
use is_builtin; use parse::{Instruction, Spirv};
use location_decoration; use spirv_search;
use name_from_id;
pub fn write_entry_point(doc: &parse::Spirv, instruction: &parse::Instruction) -> (String, String) { pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream, TokenStream) {
let (execution, id, ep_name, interface) = match instruction { let (execution, id, ep_name, interface) = match instruction {
&parse::Instruction::EntryPoint { &Instruction::EntryPoint { ref execution, id, ref name, ref interface, .. } =>
ref execution, (execution, id, name, interface),
id,
ref name,
ref interface,
..
} => {
(execution, id, name, interface)
},
_ => unreachable!(), _ => unreachable!(),
}; };
@ -36,230 +28,235 @@ pub fn write_entry_point(doc: &parse::Spirv, instruction: &parse::Instruction) -
.chain(ep_name.chars().skip(1)) .chain(ep_name.chars().skip(1))
.collect(); .collect();
let interface_structs = let ignore_first_array_in = match *execution {
write_interface_structs(doc, ExecutionModel::ExecutionModelTessellationControl => true,
&capitalized_ep_name, ExecutionModel::ExecutionModelTessellationEvaluation => true,
interface, ExecutionModel::ExecutionModelGeometry => true,
match *execution { _ => false,
enums::ExecutionModel::ExecutionModelTessellationControl => };
true, let ignore_first_array_out = match *execution {
enums::ExecutionModel::ExecutionModelTessellationEvaluation => ExecutionModel::ExecutionModelTessellationControl => true,
true, _ => false,
enums::ExecutionModel::ExecutionModelGeometry => true, };
_ => false,
}, let interface_structs = write_interface_structs(
match *execution { doc,
enums::ExecutionModel::ExecutionModelTessellationControl => &capitalized_ep_name,
true, interface,
_ => false, ignore_first_array_in,
}); ignore_first_array_out
);
let spec_consts_struct = if ::spec_consts::has_specialization_constants(doc) { let spec_consts_struct = if ::spec_consts::has_specialization_constants(doc) {
"SpecializationConstants" quote!{ SpecializationConstants }
} else { } else {
"()" quote!{ () }
}; };
let (ty, f_call) = { let (ty, f_call) = {
if let enums::ExecutionModel::ExecutionModelGLCompute = *execution { if let ExecutionModel::ExecutionModelGLCompute = *execution {
(format!("::vulkano::pipeline::shader::ComputeEntryPoint<{}, Layout>", (
spec_consts_struct), quote!{ ::vulkano::pipeline::shader::ComputeEntryPoint<#spec_consts_struct, Layout> },
format!("compute_entry_point(::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _), \ quote!{ compute_entry_point(
Layout(ShaderStages {{ compute: true, .. ShaderStages::none() }}))")) ::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
Layout(ShaderStages { compute: true, .. ShaderStages::none() })
)}
)
} else { } else {
let ty = match *execution { let entry_ty = match *execution {
enums::ExecutionModel::ExecutionModelVertex => { ExecutionModel::ExecutionModelVertex =>
"::vulkano::pipeline::shader::GraphicsShaderType::Vertex".to_owned() quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::Vertex },
},
enums::ExecutionModel::ExecutionModelTessellationControl => { ExecutionModel::ExecutionModelTessellationControl =>
"::vulkano::pipeline::shader::GraphicsShaderType::TessellationControl" quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::TessellationControl },
.to_owned()
},
enums::ExecutionModel::ExecutionModelTessellationEvaluation => { ExecutionModel::ExecutionModelTessellationEvaluation =>
"::vulkano::pipeline::shader::GraphicsShaderType::TessellationEvaluation" quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::TessellationEvaluation },
.to_owned()
},
enums::ExecutionModel::ExecutionModelGeometry => { ExecutionModel::ExecutionModelGeometry => {
let mut execution_mode = None; let mut execution_mode = None;
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
if let &parse::Instruction::ExecutionMode { if let &Instruction::ExecutionMode { target_id, ref mode, .. } = instruction {
target_id, if target_id == id {
ref mode, execution_mode = match mode {
.. &ExecutionMode::ExecutionModeInputPoints => Some(quote!{ Points }),
} = instruction &ExecutionMode::ExecutionModeInputLines => Some(quote!{ Lines }),
{ &ExecutionMode::ExecutionModeInputLinesAdjacency =>
if target_id != id { Some(quote!{ LinesWithAdjacency }),
continue; &ExecutionMode::ExecutionModeTriangles => Some(quote!{ Triangles }),
&ExecutionMode::ExecutionModeInputTrianglesAdjacency =>
Some(quote!{ TrianglesWithAdjacency }),
_ => continue,
};
break;
} }
execution_mode = match mode {
&enums::ExecutionMode::ExecutionModeInputPoints => Some("Points"),
&enums::ExecutionMode::ExecutionModeInputLines => Some("Lines"),
&enums::ExecutionMode::ExecutionModeInputLinesAdjacency =>
Some("LinesWithAdjacency"),
&enums::ExecutionMode::ExecutionModeTriangles => Some("Triangles"),
&enums::ExecutionMode::ExecutionModeInputTrianglesAdjacency =>
Some("TrianglesWithAdjacency"),
_ => continue,
};
break;
} }
} }
format!( quote!{
"::vulkano::pipeline::shader::GraphicsShaderType::Geometry( ::vulkano::pipeline::shader::GraphicsShaderType::Geometry(
\ ::vulkano::pipeline::shader::GeometryShaderExecutionMode::#execution_mode
::vulkano::pipeline::shader::GeometryShaderExecutionMode::{0} )
\ }
)", }
execution_mode.unwrap()
)
},
enums::ExecutionModel::ExecutionModelFragment => { ExecutionModel::ExecutionModelFragment =>
"::vulkano::pipeline::shader::GraphicsShaderType::Fragment".to_owned() quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::Fragment },
},
enums::ExecutionModel::ExecutionModelGLCompute => { ExecutionModel::ExecutionModelGLCompute => unreachable!(),
unreachable!()
},
enums::ExecutionModel::ExecutionModelKernel => panic!("Kernels are not supported"), ExecutionModel::ExecutionModelKernel => panic!("Kernels are not supported"),
}; };
let stage = match *execution { let stage = match *execution {
enums::ExecutionModel::ExecutionModelVertex => { ExecutionModel::ExecutionModelVertex =>
"ShaderStages { vertex: true, .. ShaderStages::none() }" quote!{ ShaderStages { vertex: true, .. ShaderStages::none() } },
},
enums::ExecutionModel::ExecutionModelTessellationControl => { ExecutionModel::ExecutionModelTessellationControl =>
"ShaderStages { tessellation_control: true, .. ShaderStages::none() }" quote!{ ShaderStages { tessellation_control: true, .. ShaderStages::none() } },
},
enums::ExecutionModel::ExecutionModelTessellationEvaluation => { ExecutionModel::ExecutionModelTessellationEvaluation =>
"ShaderStages { tessellation_evaluation: true, .. ShaderStages::none() }" quote!{ ShaderStages { tessellation_evaluation: true, .. ShaderStages::none() } },
},
enums::ExecutionModel::ExecutionModelGeometry => { ExecutionModel::ExecutionModelGeometry =>
"ShaderStages { geometry: true, .. ShaderStages::none() }" quote!{ ShaderStages { geometry: true, .. ShaderStages::none() } },
},
enums::ExecutionModel::ExecutionModelFragment => { ExecutionModel::ExecutionModelFragment =>
"ShaderStages { fragment: true, .. ShaderStages::none() }" quote!{ ShaderStages { fragment: true, .. ShaderStages::none() } },
},
enums::ExecutionModel::ExecutionModelGLCompute => unreachable!(), ExecutionModel::ExecutionModelGLCompute => unreachable!(),
enums::ExecutionModel::ExecutionModelKernel => unreachable!(), ExecutionModel::ExecutionModelKernel => unreachable!(),
}; };
let t = format!("::vulkano::pipeline::shader::GraphicsEntryPoint<{0}, {1}Input, \ let mut capitalized_ep_name_input = capitalized_ep_name.clone();
{1}Output, Layout>", capitalized_ep_name_input.push_str("Input");
spec_consts_struct, let capitalized_ep_name_input = Ident::new(&capitalized_ep_name_input, Span::call_site());
capitalized_ep_name);
let f = format!("graphics_entry_point(::std::ffi::CStr::from_ptr(NAME.as_ptr() \
as *const _), {0}Input, {0}Output, Layout({2}), {1})",
capitalized_ep_name,
ty,
stage);
(t, f) let mut capitalized_ep_name_output = capitalized_ep_name.clone();
capitalized_ep_name_output.push_str("Output");
let capitalized_ep_name_output = Ident::new(&capitalized_ep_name_output, Span::call_site());
let ty = quote!{
::vulkano::pipeline::shader::GraphicsEntryPoint<
#spec_consts_struct,
#capitalized_ep_name_input,
#capitalized_ep_name_output,
Layout>
};
let f_call = quote!{
graphics_entry_point(
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
#capitalized_ep_name_input,
#capitalized_ep_name_output,
Layout(#stage),
#entry_ty
)
};
(ty, f_call)
} }
}; };
let entry_point = format!( let mut method_name = ep_name.clone();
r#" method_name.push_str("_entry_point");
/// Returns a logical struct describing the entry point named `{ep_name}`. let method_ident = Ident::new(&method_name, Span::call_site());
#[inline]
#[allow(unsafe_code)] let ep_name_lenp1 = ep_name.chars().count() + 1;
pub fn {ep_name}_entry_point(&self) -> {ty} {{ let encoded_ep_name = ep_name.chars().map(|c| (c as u8)).collect::<Vec<_>>();
unsafe {{
#[allow(dead_code)] let entry_point = quote!{
static NAME: [u8; {ep_name_lenp1}] = [{encoded_ep_name}, 0]; // "{ep_name}" /// Returns a logical struct describing the entry point named `{ep_name}`.
self.shader.{f_call} #[inline]
}} #[allow(unsafe_code)]
}} pub fn #method_ident(&self) -> #ty {
"#, unsafe {
ep_name = ep_name, #[allow(dead_code)]
ep_name_lenp1 = ep_name.chars().count() + 1, static NAME: [u8; #ep_name_lenp1] = [ #( #encoded_ep_name ),* , 0];
ty = ty, self.shader.#f_call
encoded_ep_name = ep_name }
.chars() }
.map(|c| (c as u32).to_string()) };
.collect::<Vec<String>>()
.join(", "),
f_call = f_call
);
(interface_structs, entry_point) (interface_structs, entry_point)
} }
fn write_interface_structs(doc: &parse::Spirv, capitalized_ep_name: &str, interface: &[u32], struct Element {
location: u32,
name: String,
format: String,
location_len: usize,
}
fn write_interface_structs(doc: &Spirv, capitalized_ep_name: &str, interface: &[u32],
ignore_first_array_in: bool, ignore_first_array_out: bool) ignore_first_array_in: bool, ignore_first_array_out: bool)
-> String { -> TokenStream {
let mut input_elements = Vec::new(); let mut input_elements = vec!();
let mut output_elements = Vec::new(); let mut output_elements = vec!();
// Filling `input_elements` and `output_elements`. // Filling `input_elements` and `output_elements`.
for interface in interface.iter() { for interface in interface.iter() {
for i in doc.instructions.iter() { for i in doc.instructions.iter() {
match i { match i {
&parse::Instruction::Variable { &Instruction::Variable {
result_type_id, result_type_id,
result_id, result_id,
ref storage_class, ref storage_class,
.. ..
} if &result_id == interface => { } if &result_id == interface => {
if is_builtin(doc, result_id) { if spirv_search::is_builtin(doc, result_id) {
continue; continue;
} }
let (to_write, ignore_first_array) = match storage_class { let (to_write, ignore_first_array) = match storage_class {
&enums::StorageClass::StorageClassInput => (&mut input_elements, &StorageClass::StorageClassInput =>
ignore_first_array_in), (&mut input_elements, ignore_first_array_in),
&enums::StorageClass::StorageClassOutput => (&mut output_elements, &StorageClass::StorageClassOutput =>
ignore_first_array_out), (&mut output_elements, ignore_first_array_out),
_ => continue, _ => continue,
}; };
let name = name_from_id(doc, result_id); let name = spirv_search::name_from_id(doc, result_id);
if name == "__unnamed" { if name == "__unnamed" {
continue; continue;
} // FIXME: hack } // FIXME: hack
let loc = match location_decoration(doc, result_id) { let location = match spirv_search::location_decoration(doc, result_id) {
Some(l) => l, Some(l) => l,
None => panic!("Attribute `{}` (id {}) is missing a location", None => panic!("Attribute `{}` (id {}) is missing a location",
name, name,
result_id), result_id),
}; };
to_write let (format, location_len) = spirv_search::format_from_id(doc, result_type_id, ignore_first_array);
.push((loc, name, format_from_id(doc, result_type_id, ignore_first_array))); to_write.push(Element { location, name, format, location_len });
}, },
_ => (), _ => (),
} }
} }
} }
write_interface_struct(&format!("{}Input", capitalized_ep_name), &input_elements) + let input: TokenStream = write_interface_struct(&format!("{}Input", capitalized_ep_name), &input_elements);
&write_interface_struct(&format!("{}Output", capitalized_ep_name), &output_elements) let output: TokenStream = write_interface_struct(&format!("{}Output", capitalized_ep_name), &output_elements);
quote!{ #input #output }
} }
fn write_interface_struct(struct_name: &str, attributes: &[(u32, String, (String, usize))]) fn write_interface_struct(struct_name_str: &str, attributes: &[Element]) -> TokenStream {
-> String {
// Checking for overlapping elements. // Checking for overlapping elements.
for (offset, &(loc, ref name, (_, loc_len))) in attributes.iter().enumerate() { for (offset, element1) in attributes.iter().enumerate() {
for &(loc2, ref name2, (_, loc_len2)) in attributes.iter().skip(offset + 1) { for element2 in attributes.iter().skip(offset + 1) {
if loc == loc2 || (loc < loc2 && loc + loc_len as u32 > loc2) || if element1.location == element2.location ||
(loc2 < loc && loc2 + loc_len2 as u32 > loc) (element1.location < element2.location && element1.location + element1.location_len as u32 > element2.location) ||
(element2.location < element1.location && element2.location + element2.location_len as u32 > element1.location)
{ {
panic!("The locations of attributes `{}` (start={}, size={}) \ panic!("The locations of attributes `{}` (start={}, size={}) \
and `{}` (start={}, size={}) overlap", and `{}` (start={}, size={}) overlap",
name, element1.name,
loc, element1.location,
loc_len, element1.location_len,
name2, element2.name,
loc2, element2.location,
loc_len2); element2.location_len);
} }
} }
} }
@ -267,73 +264,67 @@ fn write_interface_struct(struct_name: &str, attributes: &[(u32, String, (String
let body = attributes let body = attributes
.iter() .iter()
.enumerate() .enumerate()
.map(|(num, &(loc, ref name, (ref ty, num_locs)))| { .map(|(num, element)| {
assert!(num_locs >= 1); assert!(element.location_len >= 1);
let loc = element.location;
let loc_end = element.location + element.location_len as u32;
let format = Ident::new(&element.format, Span::call_site());
let name = &element.name;
let num = num as u16;
format!( quote!{
"if self.num == {} {{ if self.num == #num {
self.num += 1; self.num += 1;
return Some(::vulkano::pipeline::shader::ShaderInterfaceDefEntry {{ return Some(::vulkano::pipeline::shader::ShaderInterfaceDefEntry {
location: {} .. {}, location: #loc .. #loc_end,
format: ::vulkano::format::Format::{}, format: ::vulkano::format::Format::#format,
name: Some(::std::borrow::Cow::Borrowed(\"{}\")) name: Some(::std::borrow::Cow::Borrowed(#name))
}}); });
}}", }
num, }
loc,
loc as usize + num_locs,
ty,
name
)
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>();
.join("");
format!( let struct_name = Ident::new(struct_name_str, Span::call_site());
"
let mut iter_name = struct_name.to_string();
iter_name.push_str("Iter");
let iter_name = Ident::new(&iter_name, Span::call_site());
let len = attributes.len();
quote!{
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct {name}; pub struct #struct_name;
\ #[allow(unsafe_code)]
#[allow(unsafe_code)] unsafe impl ::vulkano::pipeline::shader::ShaderInterfaceDef for #struct_name {
unsafe impl ::vulkano::pipeline::shader::ShaderInterfaceDef for \ type Iter = #iter_name;
{name} {{ fn elements(&self) -> #iter_name {
type Iter = {name}Iter; #iter_name { num: 0 }
fn elements(&self) -> {name}Iter {{ }
\ }
{name}Iter {{ num: 0 }}
}}
}}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
\ pub struct #iter_name { num: u16 }
pub struct {name}Iter {{ num: u16 }}
impl Iterator for {name}Iter {{ impl Iterator for #iter_name {
type \ type Item = ::vulkano::pipeline::shader::ShaderInterfaceDefEntry;
Item = ::vulkano::pipeline::shader::ShaderInterfaceDefEntry;
#[inline] #[inline]
\ fn next(&mut self) -> Option<Self::Item> {
fn next(&mut self) -> Option<Self::Item> {{ #( #body )*
{body}
None None
\ }
}}
#[inline] #[inline]
fn size_hint(&self) -> (usize, Option<usize>) {{ fn size_hint(&self) -> (usize, Option<usize>) {
\ let len = #len - self.num as usize;
let len = ({len} - self.num) as usize;
(len, Some(len)) (len, Some(len))
}} }
\ }
}}
impl ExactSizeIterator for {name}Iter {{}} impl ExactSizeIterator for #iter_name {}
", }
name = struct_name,
body = body,
len = attributes.len()
)
} }

View File

@ -7,21 +7,31 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
extern crate shaderc; #![recursion_limit = "1024"]
#[macro_use] extern crate quote;
extern crate shaderc;
extern crate proc_macro2;
extern crate syn;
use std::io::Error as IoError; use std::io::Error as IoError;
use syn::Ident;
use proc_macro2::{Span, TokenStream};
use shaderc::{Compiler, CompileOptions}; use shaderc::{Compiler, CompileOptions};
pub use shaderc::{CompilationArtifact, ShaderKind}; pub use shaderc::{CompilationArtifact, ShaderKind};
pub use parse::ParseError; pub use parse::ParseError;
use parse::Instruction;
use enums::Capability;
mod descriptor_sets; mod descriptor_sets;
mod entry_point; mod entry_point;
mod enums; mod enums;
mod parse; mod parse;
mod spec_consts; mod spec_consts;
mod structs; mod structs;
mod spirv_search;
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> { pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?; let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?;
@ -34,12 +44,41 @@ pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String
Ok(content) Ok(content)
} }
pub fn reflect(name: &str, spirv: &[u32]) -> Result<String, Error> { pub fn reflect(name: &str, spirv: &[u32]) -> Result<TokenStream, Error> {
let struct_name = Ident::new(&name, Span::call_site());
let doc = parse::parse_spirv(spirv)?; let doc = parse::parse_spirv(spirv)?;
let mut output = String::new(); // checking whether each required capability is enabled in the Vulkan device
output.push_str( let mut cap_checks: Vec<TokenStream> = vec!();
r#" for i in doc.instructions.iter() {
if let &Instruction::Capability(ref cap) = i {
if let Some(cap_string) = capability_name(cap) {
let cap = Ident::new(cap_string, Span::call_site());
cap_checks.push(quote!{
if !device.enabled_features().#cap {
panic!("capability {:?} not enabled", #cap_string); // TODO: error
//return Err(CapabilityNotEnabled);
}
});
}
}
}
// writing one method for each entry point of this module
let mut entry_points_inside_impl: Vec<TokenStream> = vec!();
let mut entry_points_outside_impl: Vec<TokenStream> = vec!();
for instruction in doc.instructions.iter() {
if let &Instruction::EntryPoint { .. } = instruction {
let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction);
entry_points_inside_impl.push(entry_point);
entry_points_outside_impl.push(outside);
}
}
let structs = structs::write_structs(&doc);
let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc);
let specialization_constants = spec_consts::write_specialization_constants(&doc);
let ast = quote!{
#[allow(unused_imports)] #[allow(unused_imports)]
use std::sync::Arc; use std::sync::Arc;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -77,106 +116,51 @@ pub fn reflect(name: &str, spirv: &[u32]) -> Result<String, Error> {
use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait; use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait;
#[allow(unused_imports)] #[allow(unused_imports)]
use vulkano::pipeline::shader::SpecializationMapEntry; use vulkano::pipeline::shader::SpecializationMapEntry;
"#,
);
{ pub struct #struct_name {
// contains the data that was passed as input to this function shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
let spirv_words = spirv.iter() }
.map(|&word| word.to_string())
.collect::<Vec<String>>()
.join(", ");
// writing the header impl #struct_name {
output.push_str(&format!( /// Loads the shader in Vulkan as a `ShaderModule`.
r#" #[inline]
pub struct {name} {{ #[allow(unsafe_code)]
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>, pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>)
}} -> Result<#struct_name, ::vulkano::OomError>
{
#( #cap_checks )*
let words = [ #( #spirv ),* ];
impl {name} {{ unsafe {
/// Loads the shader in Vulkan as a `ShaderModule`. Ok(#struct_name {
#[inline] shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
#[allow(unsafe_code)] })
pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>)
-> Result<{name}, ::vulkano::OomError>
{{
"#,
name = name
));
// checking whether each required capability is enabled in the Vulkan device
for i in doc.instructions.iter() {
if let &parse::Instruction::Capability(ref cap) = i {
if let Some(cap) = capability_name(cap) {
output.push_str(&format!(
r#"
if !device.enabled_features().{cap} {{
panic!("capability {{:?}} not enabled", "{cap}") // FIXME: error
//return Err(CapabilityNotEnabled);
}}"#,
cap = cap
));
} }
} }
}
// follow-up of the header /// Returns the module that was created.
output.push_str(&format!( #[allow(dead_code)]
r#" #[inline]
unsafe {{ pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {
let words = [{spirv_words}]; &self.shader
Ok({name} {{
shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
}})
}}
}}
/// Returns the module that was created.
#[allow(dead_code)]
#[inline]
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {{
&self.shader
}}
"#,
name = name,
spirv_words = spirv_words
));
// writing one method for each entry point of this module
let mut outside_impl = String::new();
for instruction in doc.instructions.iter() {
if let &parse::Instruction::EntryPoint { .. } = instruction {
let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction);
output.push_str(&entry_point);
outside_impl.push_str(&outside);
} }
#( #entry_points_inside_impl )*
} }
// footer #( #entry_points_outside_impl )*
output.push_str(&format!(
r#"
}}
"#
));
output.push_str(&outside_impl); pub mod ty {
#structs
}
// struct definitions #descriptor_sets
output.push_str("pub mod ty {"); #specialization_constants
output.push_str(&structs::write_structs(&doc)); };
output.push_str("}");
// descriptor sets //println!("{}", ast.to_string());
output.push_str(&descriptor_sets::write_descriptor_sets(&doc));
// specialization constants Ok(ast)
output.push_str(&spec_consts::write_specialization_constants(&doc));
}
Ok(output)
} }
#[derive(Debug)] #[derive(Debug)]
@ -199,301 +183,78 @@ impl From<ParseError> for Error {
} }
} }
/// Returns the vulkano `Format` and number of occupied locations from an id.
///
/// If `ignore_first_array` is true, the function expects the outermost instruction to be
/// `OpTypeArray`. If it's the case, the OpTypeArray will be ignored. If not, the function will
/// panic.
fn format_from_id(doc: &parse::Spirv, searched: u32, ignore_first_array: bool) -> (String, usize) {
for instruction in doc.instructions.iter() {
match instruction {
&parse::Instruction::TypeInt {
result_id,
width,
signedness,
} if result_id == searched => {
assert!(!ignore_first_array);
return (match (width, signedness) {
(8, true) => "R8Sint",
(8, false) => "R8Uint",
(16, true) => "R16Sint",
(16, false) => "R16Uint",
(32, true) => "R32Sint",
(32, false) => "R32Uint",
(64, true) => "R64Sint",
(64, false) => "R64Uint",
_ => panic!(),
}.to_owned(),
1);
},
&parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
assert!(!ignore_first_array);
return (match width {
32 => "R32Sfloat",
64 => "R64Sfloat",
_ => panic!(),
}.to_owned(),
1);
},
&parse::Instruction::TypeVector {
result_id,
component_id,
count,
} if result_id == searched => {
assert!(!ignore_first_array);
let (format, sz) = format_from_id(doc, component_id, false);
assert!(format.starts_with("R32"));
assert_eq!(sz, 1);
let format = if count == 1 {
format
} else if count == 2 {
format!("R32G32{}", &format[3 ..])
} else if count == 3 {
format!("R32G32B32{}", &format[3 ..])
} else if count == 4 {
format!("R32G32B32A32{}", &format[3 ..])
} else {
panic!("Found vector type with more than 4 elements")
};
return (format, sz);
},
&parse::Instruction::TypeMatrix {
result_id,
column_type_id,
column_count,
} if result_id == searched => {
assert!(!ignore_first_array);
let (format, sz) = format_from_id(doc, column_type_id, false);
return (format, sz * column_count as usize);
},
&parse::Instruction::TypeArray {
result_id,
type_id,
length_id,
} if result_id == searched => {
if ignore_first_array {
return format_from_id(doc, type_id, false);
}
let (format, sz) = format_from_id(doc, type_id, false);
let len = doc.instructions
.iter()
.filter_map(|e| match e {
&parse::Instruction::Constant {
result_id,
ref data,
..
} if result_id == length_id => Some(data.clone()),
_ => None,
})
.next()
.expect("failed to find array length");
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
return (format, sz * len as usize);
},
&parse::Instruction::TypePointer { result_id, type_id, .. }
if result_id == searched => {
return format_from_id(doc, type_id, ignore_first_array);
},
_ => (),
}
}
panic!("Type #{} not found or invalid", searched)
}
fn name_from_id(doc: &parse::Spirv, searched: u32) -> String {
doc.instructions
.iter()
.filter_map(|i| if let &parse::Instruction::Name {
target_id,
ref name,
} = i
{
if target_id == searched {
Some(name.clone())
} else {
None
}
} else {
None
})
.next()
.and_then(|n| if !n.is_empty() { Some(n) } else { None })
.unwrap_or("__unnamed".to_owned())
}
fn member_name_from_id(doc: &parse::Spirv, searched: u32, searched_member: u32) -> String {
doc.instructions
.iter()
.filter_map(|i| if let &parse::Instruction::MemberName {
target_id,
member,
ref name,
} = i
{
if target_id == searched && member == searched_member {
Some(name.clone())
} else {
None
}
} else {
None
})
.next()
.and_then(|n| if !n.is_empty() { Some(n) } else { None })
.unwrap_or("__unnamed".to_owned())
}
fn location_decoration(doc: &parse::Spirv, searched: u32) -> Option<u32> {
doc.instructions
.iter()
.filter_map(|i| if let &parse::Instruction::Decorate {
target_id,
decoration: enums::Decoration::DecorationLocation,
ref params,
} = i
{
if target_id == searched {
Some(params[0])
} else {
None
}
} else {
None
})
.next()
}
/// Returns true if a `BuiltIn` decorator is applied on an id.
fn is_builtin(doc: &parse::Spirv, id: u32) -> bool {
for instruction in &doc.instructions {
match *instruction {
parse::Instruction::Decorate {
target_id,
decoration: enums::Decoration::DecorationBuiltIn,
..
} if target_id == id => {
return true;
},
parse::Instruction::MemberDecorate {
target_id,
decoration: enums::Decoration::DecorationBuiltIn,
..
} if target_id == id => {
return true;
},
_ => (),
}
}
for instruction in &doc.instructions {
match *instruction {
parse::Instruction::Variable {
result_type_id,
result_id,
..
} if result_id == id => {
return is_builtin(doc, result_type_id);
},
parse::Instruction::TypeArray { result_id, type_id, .. } if result_id == id => {
return is_builtin(doc, type_id);
},
parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == id => {
return is_builtin(doc, type_id);
},
parse::Instruction::TypeStruct {
result_id,
ref member_types,
} if result_id == id => {
for &mem in member_types {
if is_builtin(doc, mem) {
return true;
}
}
},
parse::Instruction::TypePointer { result_id, type_id, .. } if result_id == id => {
return is_builtin(doc, type_id);
},
_ => (),
}
}
false
}
/// Returns the name of the Vulkan something that corresponds to an `OpCapability`. /// Returns the name of the Vulkan something that corresponds to an `OpCapability`.
/// ///
/// Returns `None` if irrelevant. /// Returns `None` if irrelevant.
// TODO: this function is a draft, as the actual names may not be the same // TODO: this function is a draft, as the actual names may not be the same
fn capability_name(cap: &enums::Capability) -> Option<&'static str> { fn capability_name(cap: &Capability) -> Option<&'static str> {
match *cap { match *cap {
enums::Capability::CapabilityMatrix => None, // always supported Capability::CapabilityMatrix => None, // always supported
enums::Capability::CapabilityShader => None, // always supported Capability::CapabilityShader => None, // always supported
enums::Capability::CapabilityGeometry => Some("geometry_shader"), Capability::CapabilityGeometry => Some("geometry_shader"),
enums::Capability::CapabilityTessellation => Some("tessellation_shader"), Capability::CapabilityTessellation => Some("tessellation_shader"),
enums::Capability::CapabilityAddresses => panic!(), // not supported Capability::CapabilityAddresses => panic!(), // not supported
enums::Capability::CapabilityLinkage => panic!(), // not supported Capability::CapabilityLinkage => panic!(), // not supported
enums::Capability::CapabilityKernel => panic!(), // not supported Capability::CapabilityKernel => panic!(), // not supported
enums::Capability::CapabilityVector16 => panic!(), // not supported Capability::CapabilityVector16 => panic!(), // not supported
enums::Capability::CapabilityFloat16Buffer => panic!(), // not supported Capability::CapabilityFloat16Buffer => panic!(), // not supported
enums::Capability::CapabilityFloat16 => panic!(), // not supported Capability::CapabilityFloat16 => panic!(), // not supported
enums::Capability::CapabilityFloat64 => Some("shader_f3264"), Capability::CapabilityFloat64 => Some("shader_f3264"),
enums::Capability::CapabilityInt64 => Some("shader_int64"), Capability::CapabilityInt64 => Some("shader_int64"),
enums::Capability::CapabilityInt64Atomics => panic!(), // not supported Capability::CapabilityInt64Atomics => panic!(), // not supported
enums::Capability::CapabilityImageBasic => panic!(), // not supported Capability::CapabilityImageBasic => panic!(), // not supported
enums::Capability::CapabilityImageReadWrite => panic!(), // not supported Capability::CapabilityImageReadWrite => panic!(), // not supported
enums::Capability::CapabilityImageMipmap => panic!(), // not supported Capability::CapabilityImageMipmap => panic!(), // not supported
enums::Capability::CapabilityPipes => panic!(), // not supported Capability::CapabilityPipes => panic!(), // not supported
enums::Capability::CapabilityGroups => panic!(), // not supported Capability::CapabilityGroups => panic!(), // not supported
enums::Capability::CapabilityDeviceEnqueue => panic!(), // not supported Capability::CapabilityDeviceEnqueue => panic!(), // not supported
enums::Capability::CapabilityLiteralSampler => panic!(), // not supported Capability::CapabilityLiteralSampler => panic!(), // not supported
enums::Capability::CapabilityAtomicStorage => panic!(), // not supported Capability::CapabilityAtomicStorage => panic!(), // not supported
enums::Capability::CapabilityInt16 => Some("shader_int16"), Capability::CapabilityInt16 => Some("shader_int16"),
enums::Capability::CapabilityTessellationPointSize => Capability::CapabilityTessellationPointSize =>
Some("shader_tessellation_and_geometry_point_size"), Some("shader_tessellation_and_geometry_point_size"),
enums::Capability::CapabilityGeometryPointSize => Capability::CapabilityGeometryPointSize =>
Some("shader_tessellation_and_geometry_point_size"), Some("shader_tessellation_and_geometry_point_size"),
enums::Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"), Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"),
enums::Capability::CapabilityStorageImageMultisample => Capability::CapabilityStorageImageMultisample =>
Some("shader_storage_image_multisample"), Some("shader_storage_image_multisample"),
enums::Capability::CapabilityUniformBufferArrayDynamicIndexing => Capability::CapabilityUniformBufferArrayDynamicIndexing =>
Some("shader_uniform_buffer_array_dynamic_indexing"), Some("shader_uniform_buffer_array_dynamic_indexing"),
enums::Capability::CapabilitySampledImageArrayDynamicIndexing => Capability::CapabilitySampledImageArrayDynamicIndexing =>
Some("shader_sampled_image_array_dynamic_indexing"), Some("shader_sampled_image_array_dynamic_indexing"),
enums::Capability::CapabilityStorageBufferArrayDynamicIndexing => Capability::CapabilityStorageBufferArrayDynamicIndexing =>
Some("shader_storage_buffer_array_dynamic_indexing"), Some("shader_storage_buffer_array_dynamic_indexing"),
enums::Capability::CapabilityStorageImageArrayDynamicIndexing => Capability::CapabilityStorageImageArrayDynamicIndexing =>
Some("shader_storage_image_array_dynamic_indexing"), Some("shader_storage_image_array_dynamic_indexing"),
enums::Capability::CapabilityClipDistance => Some("shader_clip_distance"), Capability::CapabilityClipDistance => Some("shader_clip_distance"),
enums::Capability::CapabilityCullDistance => Some("shader_cull_distance"), Capability::CapabilityCullDistance => Some("shader_cull_distance"),
enums::Capability::CapabilityImageCubeArray => Some("image_cube_array"), Capability::CapabilityImageCubeArray => Some("image_cube_array"),
enums::Capability::CapabilitySampleRateShading => Some("sample_rate_shading"), Capability::CapabilitySampleRateShading => Some("sample_rate_shading"),
enums::Capability::CapabilityImageRect => panic!(), // not supported Capability::CapabilityImageRect => panic!(), // not supported
enums::Capability::CapabilitySampledRect => panic!(), // not supported Capability::CapabilitySampledRect => panic!(), // not supported
enums::Capability::CapabilityGenericPointer => panic!(), // not supported Capability::CapabilityGenericPointer => panic!(), // not supported
enums::Capability::CapabilityInt8 => panic!(), // not supported Capability::CapabilityInt8 => panic!(), // not supported
enums::Capability::CapabilityInputAttachment => None, // always supported Capability::CapabilityInputAttachment => None, // always supported
enums::Capability::CapabilitySparseResidency => Some("shader_resource_residency"), Capability::CapabilitySparseResidency => Some("shader_resource_residency"),
enums::Capability::CapabilityMinLod => Some("shader_resource_min_lod"), Capability::CapabilityMinLod => Some("shader_resource_min_lod"),
enums::Capability::CapabilitySampled1D => None, // always supported Capability::CapabilitySampled1D => None, // always supported
enums::Capability::CapabilityImage1D => None, // always supported Capability::CapabilityImage1D => None, // always supported
enums::Capability::CapabilitySampledCubeArray => Some("image_cube_array"), Capability::CapabilitySampledCubeArray => Some("image_cube_array"),
enums::Capability::CapabilitySampledBuffer => None, // always supported Capability::CapabilitySampledBuffer => None, // always supported
enums::Capability::CapabilityImageBuffer => None, // always supported Capability::CapabilityImageBuffer => None, // always supported
enums::Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"), Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"),
enums::Capability::CapabilityStorageImageExtendedFormats => Capability::CapabilityStorageImageExtendedFormats =>
Some("shader_storage_image_extended_formats"), Some("shader_storage_image_extended_formats"),
enums::Capability::CapabilityImageQuery => None, // always supported Capability::CapabilityImageQuery => None, // always supported
enums::Capability::CapabilityDerivativeControl => None, // always supported Capability::CapabilityDerivativeControl => None, // always supported
enums::Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"), Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"),
enums::Capability::CapabilityTransformFeedback => panic!(), // not supported Capability::CapabilityTransformFeedback => panic!(), // not supported
enums::Capability::CapabilityGeometryStreams => panic!(), // not supported Capability::CapabilityGeometryStreams => panic!(), // not supported
enums::Capability::CapabilityStorageImageReadWithoutFormat => Capability::CapabilityStorageImageReadWithoutFormat =>
Some("shader_storage_image_read_without_format"), Some("shader_storage_image_read_without_format"),
enums::Capability::CapabilityStorageImageWriteWithoutFormat => Capability::CapabilityStorageImageWriteWithoutFormat =>
Some("shader_storage_image_write_without_format"), Some("shader_storage_image_write_without_format"),
enums::Capability::CapabilityMultiViewport => Some("multi_viewport"), Capability::CapabilityMultiViewport => Some("multi_viewport"),
} }
} }

View File

@ -9,17 +9,22 @@
use std::mem; use std::mem;
use enums; use syn::Ident;
use parse; use proc_macro2::{Span, TokenStream};
use enums::Decoration;
use parse::{Instruction, Spirv};
use spirv_search;
use structs;
/// Returns true if the document has specialization constants. /// Returns true if the document has specialization constants.
pub fn has_specialization_constants(doc: &parse::Spirv) -> bool { pub fn has_specialization_constants(doc: &Spirv) -> bool {
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
match instruction { match instruction {
&parse::Instruction::SpecConstantTrue { .. } => return true, &Instruction::SpecConstantTrue { .. } => return true,
&parse::Instruction::SpecConstantFalse { .. } => return true, &Instruction::SpecConstantFalse { .. } => return true,
&parse::Instruction::SpecConstant { .. } => return true, &Instruction::SpecConstant { .. } => return true,
&parse::Instruction::SpecConstantComposite { .. } => return true, &Instruction::SpecConstantComposite { .. } => return true,
_ => (), _ => (),
} }
} }
@ -29,56 +34,38 @@ pub fn has_specialization_constants(doc: &parse::Spirv) -> bool {
/// Writes the `SpecializationConstants` struct that contains the specialization constants and /// Writes the `SpecializationConstants` struct that contains the specialization constants and
/// implements the `Default` and the `vulkano::pipeline::shader::SpecializationConstants` traits. /// implements the `Default` and the `vulkano::pipeline::shader::SpecializationConstants` traits.
pub fn write_specialization_constants(doc: &parse::Spirv) -> String { pub fn write_specialization_constants(doc: &Spirv) -> TokenStream {
struct SpecConst { struct SpecConst {
name: String, name: String,
constant_id: u32, constant_id: u32,
rust_ty: String, rust_ty: TokenStream,
rust_size: usize, rust_size: usize,
rust_alignment: usize, rust_alignment: u32,
default_value: String, default_value: TokenStream,
} }
let mut spec_consts = Vec::new(); let mut spec_consts = Vec::new();
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
let (type_id, result_id, default_value) = match instruction { let (type_id, result_id, default_value) = match instruction {
&parse::Instruction::SpecConstantTrue { &Instruction::SpecConstantTrue { result_type_id, result_id } =>
result_type_id, (result_type_id, result_id, quote!{1u32}),
result_id,
} => { &Instruction::SpecConstantFalse { result_type_id, result_id } =>
(result_type_id, result_id, "1u32".to_string()) (result_type_id, result_id, quote!{0u32}),
},
&parse::Instruction::SpecConstantFalse { &Instruction::SpecConstant { result_type_id, result_id, ref data } => {
result_type_id, let def_val = quote!{
result_id, unsafe {{ ::std::mem::transmute([ #( #data ),* ]) }}
} => { };
(result_type_id, result_id, "0u32".to_string())
},
&parse::Instruction::SpecConstant {
result_type_id,
result_id,
ref data,
} => {
let data = data.iter()
.map(|d| d.to_string() + "u32")
.collect::<Vec<_>>()
.join(", ");
let def_val = format!("unsafe {{ ::std::mem::transmute([{}]) }}", data);
(result_type_id, result_id, def_val) (result_type_id, result_id, def_val)
}, }
&parse::Instruction::SpecConstantComposite { &Instruction::SpecConstantComposite { result_type_id, result_id, ref data } => {
result_type_id, let def_val = quote!{
result_id, unsafe {{ ::std::mem::transmute([ #( #data ),* ]) }}
ref data, };
} => {
let data = data.iter()
.map(|d| d.to_string() + "u32")
.collect::<Vec<_>>()
.join(", ");
let def_val = format!("unsafe {{ ::std::mem::transmute([{}]) }}", data);
(result_type_id, result_id, def_val) (result_type_id, result_id, def_val)
}, }
_ => continue, _ => continue,
}; };
@ -88,105 +75,93 @@ pub fn write_specialization_constants(doc: &parse::Spirv) -> String {
let constant_id = doc.instructions let constant_id = doc.instructions
.iter() .iter()
.filter_map(|i| match i { .filter_map(|i| match i {
&parse::Instruction::Decorate { &Instruction::Decorate { target_id, decoration: Decoration::DecorationSpecId, ref params }
target_id, if target_id == result_id => Some(params[0]),
decoration: enums::Decoration::DecorationSpecId, _ => None,
ref params, })
} if target_id == result_id => {
Some(params[0])
},
_ => None,
})
.next() .next()
.expect("Found a specialization constant with no SpecId decoration"); .expect("Found a specialization constant with no SpecId decoration");
spec_consts.push(SpecConst { spec_consts.push(SpecConst {
name: ::name_from_id(doc, result_id), name: spirv_search::name_from_id(doc, result_id),
constant_id, constant_id,
rust_ty, rust_ty,
rust_size, rust_size,
rust_alignment, rust_alignment: rust_alignment as u32,
default_value, default_value,
}); });
} }
let map_entries = { let map_entries = {
let mut map_entries = Vec::new(); let mut map_entries = Vec::new();
let mut curr_offset = 0; let mut curr_offset = 0;
for c in &spec_consts { for spec_const in &spec_consts {
map_entries.push(format!( let constant_id = spec_const.constant_id;
"SpecializationMapEntry {{ let rust_size = spec_const.rust_size;
constant_id: \ map_entries.push(quote!{
{}, SpecializationMapEntry {
offset: {}, constant_id: #constant_id,
size: {}, offset: #curr_offset,
\ size: #rust_size,
}}", }
c.constant_id, });
curr_offset,
c.rust_size
));
assert_ne!(c.rust_size, 0); assert_ne!(spec_const.rust_size, 0);
curr_offset += c.rust_size; curr_offset += spec_const.rust_size as u32;
curr_offset = c.rust_alignment * (1 + (curr_offset - 1) / c.rust_alignment); curr_offset = spec_const.rust_alignment * (1 + (curr_offset - 1) / spec_const.rust_alignment);
} }
map_entries map_entries
}; };
format!( let num_map_entries = map_entries.len();
r#"
#[derive(Debug, Copy, Clone)] let mut struct_members = vec!();
#[allow(non_snake_case)] let mut struct_member_defaults = vec!();
#[repr(C)] for spec_const in spec_consts {
pub struct SpecializationConstants {{ let name = Ident::new(&spec_const.name, Span::call_site());
{struct_def} let rust_ty = spec_const.rust_ty;
}} let default_value = spec_const.default_value;
struct_members.push(quote!{ pub #name: #rust_ty });
struct_member_defaults.push(quote!{ #name: #default_value });
}
impl Default for SpecializationConstants {{ quote!{
fn default() -> SpecializationConstants {{ #[derive(Debug, Copy, Clone)]
SpecializationConstants {{ #[allow(non_snake_case)]
{def_vals} #[repr(C)]
}} pub struct SpecializationConstants {
}} #( #struct_members ),*
}} }
unsafe impl SpecConstsTrait for SpecializationConstants {{ impl Default for SpecializationConstants {
fn descriptors() -> &'static [SpecializationMapEntry] {{ fn default() -> SpecializationConstants {
static DESCRIPTORS: [SpecializationMapEntry; {num_map_entries}] = [ SpecializationConstants {
{map_entries} #( #struct_member_defaults ),*
]; }
&DESCRIPTORS }
}} }
}}
"#, unsafe impl SpecConstsTrait for SpecializationConstants {
struct_def = spec_consts fn descriptors() -> &'static [SpecializationMapEntry] {
.iter() static DESCRIPTORS: [SpecializationMapEntry; #num_map_entries] = [
.map(|c| format!("pub {}: {}", c.name, c.rust_ty)) #( #map_entries ),*
.collect::<Vec<_>>() ];
.join(", "), &DESCRIPTORS
def_vals = spec_consts }
.iter() }
.map(|c| format!("{}: {}", c.name, c.default_value)) }
.collect::<Vec<_>>()
.join(", "),
num_map_entries = map_entries.len(),
map_entries = map_entries.join(", ")
)
} }
// Wrapper around `type_from_id` that also handles booleans. // Wrapper around `type_from_id` that also handles booleans.
fn spec_const_type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) { fn spec_const_type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>, usize) {
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
match instruction { match instruction {
&parse::Instruction::TypeBool { result_id } if result_id == searched => { &Instruction::TypeBool { result_id } if result_id == searched => {
return ("u32".to_owned(), Some(mem::size_of::<u32>()), mem::align_of::<u32>()); return (quote!{u32}, Some(mem::size_of::<u32>()), mem::align_of::<u32>());
}, },
_ => (), _ => (),
} }
} }
::structs::type_from_id(doc, searched) structs::type_from_id(doc, searched)
} }

View File

@ -0,0 +1,190 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use parse::{Instruction, Spirv};
use enums::Decoration;
/// Returns the vulkano `Format` and number of occupied locations from an id.
///
/// If `ignore_first_array` is true, the function expects the outermost instruction to be
/// `OpTypeArray`. If it's the case, the OpTypeArray will be ignored. If not, the function will
/// panic.
pub fn format_from_id(doc: &Spirv, searched: u32, ignore_first_array: bool) -> (String, usize) {
for instruction in doc.instructions.iter() {
match instruction {
&Instruction::TypeInt {
result_id,
width,
signedness,
} if result_id == searched => {
assert!(!ignore_first_array);
let format = match (width, signedness) {
(8, true) => "R8Sint",
(8, false) => "R8Uint",
(16, true) => "R16Sint",
(16, false) => "R16Uint",
(32, true) => "R32Sint",
(32, false) => "R32Uint",
(64, true) => "R64Sint",
(64, false) => "R64Uint",
_ => panic!(),
};
return (format.to_string(), 1);
},
&Instruction::TypeFloat { result_id, width } if result_id == searched => {
assert!(!ignore_first_array);
let format = match width {
32 => "R32Sfloat",
64 => "R64Sfloat",
_ => panic!(),
};
return (format.to_string(), 1);
}
&Instruction::TypeVector {
result_id,
component_id,
count,
} if result_id == searched => {
assert!(!ignore_first_array);
let (format, sz) = format_from_id(doc, component_id, false);
assert!(format.starts_with("R32"));
assert_eq!(sz, 1);
let format = match count {
1 => format,
2 => format!("R32G32{}", &format[3 ..]),
3 => format!("R32G32B32{}", &format[3 ..]),
4 => format!("R32G32B32A32{}", &format[3 ..]),
_ => panic!("Found vector type with more than 4 elements")
};
return (format, sz);
},
&Instruction::TypeMatrix {
result_id,
column_type_id,
column_count,
} if result_id == searched => {
assert!(!ignore_first_array);
let (format, sz) = format_from_id(doc, column_type_id, false);
return (format, sz * column_count as usize);
},
&Instruction::TypeArray {
result_id,
type_id,
length_id,
} if result_id == searched => {
if ignore_first_array {
return format_from_id(doc, type_id, false);
}
let (format, sz) = format_from_id(doc, type_id, false);
let len = doc.instructions
.iter()
.filter_map(|e| match e {
&Instruction::Constant { result_id, ref data, .. }
if result_id == length_id => Some(data.clone()),
_ => None,
})
.next()
.expect("failed to find array length");
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
return (format, sz * len as usize);
},
&Instruction::TypePointer { result_id, type_id, .. }
if result_id == searched => {
return format_from_id(doc, type_id, ignore_first_array);
},
_ => (),
}
}
panic!("Type #{} not found or invalid", searched)
}
pub fn name_from_id(doc: &Spirv, searched: u32) -> String {
for instruction in &doc.instructions {
if let &Instruction::Name { target_id, ref name } = instruction {
if target_id == searched {
return name.clone()
}
}
}
String::from("__unnamed")
}
pub fn member_name_from_id(doc: &Spirv, searched: u32, searched_member: u32) -> String {
for instruction in &doc.instructions {
if let &Instruction::MemberName { target_id, member, ref name } = instruction {
if target_id == searched && member == searched_member {
return name.clone()
}
}
}
String::from("__unnamed")
}
pub fn location_decoration(doc: &Spirv, searched: u32) -> Option<u32> {
for instruction in &doc.instructions {
if let &Instruction::Decorate { target_id, decoration: Decoration::DecorationLocation, ref params } = instruction {
if target_id == searched {
return Some(params[0])
}
}
}
None
}
/// Returns true if a `BuiltIn` decorator is applied on an id.
pub fn is_builtin(doc: &Spirv, id: u32) -> bool {
for instruction in &doc.instructions {
match *instruction {
Instruction::Decorate { target_id, decoration: Decoration::DecorationBuiltIn, .. }
if target_id == id => { return true }
Instruction::MemberDecorate { target_id, decoration: Decoration::DecorationBuiltIn, .. }
if target_id == id => { return true }
_ => (),
}
}
for instruction in &doc.instructions {
match *instruction {
Instruction::Variable {
result_type_id,
result_id,
..
} if result_id == id => {
return is_builtin(doc, result_type_id);
}
Instruction::TypeArray { result_id, type_id, .. } if result_id == id => {
return is_builtin(doc, type_id);
}
Instruction::TypeRuntimeArray { result_id, type_id } if result_id == id => {
return is_builtin(doc, type_id);
}
Instruction::TypeStruct {
result_id,
ref member_types,
} if result_id == id => {
for &mem in member_types {
if is_builtin(doc, mem) {
return true;
}
}
}
Instruction::TypePointer { result_id, type_id, .. } if result_id == id => {
return is_builtin(doc, type_id);
}
_ => ()
}
}
false
}

View File

@ -9,55 +9,38 @@
use std::mem; use std::mem;
use enums; use syn::Ident;
use parse; use proc_macro2::{Span, TokenStream};
use parse::{Instruction, Spirv};
use enums::Decoration;
use spirv_search;
/// Translates all the structs that are contained in the SPIR-V document as Rust structs. /// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &parse::Spirv) -> String { pub fn write_structs(doc: &Spirv) -> TokenStream {
let mut result = String::new(); let mut structs = vec!();
for instruction in &doc.instructions { for instruction in &doc.instructions {
match *instruction { match *instruction {
parse::Instruction::TypeStruct { Instruction::TypeStruct { result_id, ref member_types } =>
result_id, structs.push(write_struct(doc, result_id, member_types).0),
ref member_types, _ => ()
} => {
let (s, _) = write_struct(doc, result_id, member_types);
result.push_str(&s);
result.push_str("\n");
},
_ => (),
} }
} }
result quote!{
} #( #structs )*
/// Represents a rust struct member
struct Member {
name: String,
value: String,
offset: Option<usize>,
}
impl Member {
fn declaration_text(&self) -> String {
let offset = match self.offset {
Some(o) => format!("/* offset: {} */", o),
_ => "".to_owned(),
};
format!(" pub {}: {} {}", self.name, self.value, offset)
}
fn copy_text(&self) -> String {
format!(" {name}: self.{name}", name = self.name)
} }
} }
/// Analyzes a single struct, returns a string containing its Rust definition, plus its size. /// Analyzes a single struct, returns a string containing its Rust definition, plus its size.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String, Option<usize>) { fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, Option<usize>) {
let name = ::name_from_id(doc, struct_id); let name = Ident::new(&spirv_search::name_from_id(doc, struct_id), Span::call_site());
// The members of this struct. // The members of this struct.
struct Member {
pub name: Ident,
pub ty: TokenStream,
}
let mut rust_members = Vec::with_capacity(members.len()); let mut rust_members = Vec::with_capacity(members.len());
// Padding structs will be named `_paddingN` where `N` is determined by this variable. // Padding structs will be named `_paddingN` where `N` is determined by this variable.
@ -70,12 +53,12 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
for (num, &member) in members.iter().enumerate() { for (num, &member) in members.iter().enumerate() {
// Compute infos about the member. // Compute infos about the member.
let (ty, rust_size, rust_align) = type_from_id(doc, member); let (ty, rust_size, rust_align) = type_from_id(doc, member);
let member_name = ::member_name_from_id(doc, struct_id, num as u32); let member_name = spirv_search::member_name_from_id(doc, struct_id, num as u32);
// Ignore the whole struct is a member is built in, which includes // Ignore the whole struct is a member is built in, which includes
// `gl_Position` for example. // `gl_Position` for example.
if is_builtin_member(doc, struct_id, num as u32) { if is_builtin_member(doc, struct_id, num as u32) {
return (String::new(), None); // TODO: is this correct? shouldn't it return a correct struct but with a flag or something? return (quote!{}, None); // TODO: is this correct? shouldn't it return a correct struct but with a flag or something?
} }
// Finding offset of the current member, as requested by the SPIR-V code. // Finding offset of the current member, as requested by the SPIR-V code.
@ -83,10 +66,10 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
.iter() .iter()
.filter_map(|i| { .filter_map(|i| {
match *i { match *i {
parse::Instruction::MemberDecorate { Instruction::MemberDecorate {
target_id, target_id,
member, member,
decoration: enums::Decoration::DecorationOffset, decoration: Decoration::DecorationOffset,
ref params, ref params,
} if target_id == struct_id && member as usize == num => { } if target_id == struct_id && member as usize == num => {
return Some(params[0]); return Some(params[0]);
@ -102,7 +85,7 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
// variables only. Ignoring these. // variables only. Ignoring these.
let spirv_offset = match spirv_offset { let spirv_offset = match spirv_offset {
Some(o) => o as usize, Some(o) => o as usize,
None => return (String::new(), None), // TODO: shouldn't we return and let the caller ignore it instead? None => return (quote!{}, None), // TODO: shouldn't we return and let the caller ignore it instead?
}; };
// We need to add a dummy field if necessary. // We need to add a dummy field if necessary.
@ -124,10 +107,9 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
let padding_num = next_padding_num; let padding_num = next_padding_num;
next_padding_num += 1; next_padding_num += 1;
rust_members.push(Member { rust_members.push(Member {
name: format!("_dummy{}", padding_num), name: Ident::new(&format!("_dummy{}", padding_num), Span::call_site()),
value: format!("[u8; {}]", diff), ty: quote!{ [u8; #diff] },
offset: None, });
});
*current_rust_offset += diff; *current_rust_offset += diff;
} }
} }
@ -140,29 +122,28 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
} }
rust_members.push(Member { rust_members.push(Member {
name: member_name.to_owned(), name: Ident::new(&member_name, Span::call_site()),
value: ty, ty,
offset: Some(spirv_offset), });
});
} }
// Try determine the total size of the struct in order to add padding at the end of the struct. // Try determine the total size of the struct in order to add padding at the end of the struct.
let spirv_req_total_size = doc.instructions let spirv_req_total_size = doc.instructions
.iter() .iter()
.filter_map(|i| match *i { .filter_map(|i| match *i {
parse::Instruction::Decorate { Instruction::Decorate {
target_id, target_id,
decoration: enums::Decoration::DecorationArrayStride, decoration: Decoration::DecorationArrayStride,
ref params, ref params,
} => { } => {
for inst in doc.instructions.iter() { for inst in doc.instructions.iter() {
match *inst { match *inst {
parse::Instruction::TypeArray { Instruction::TypeArray {
result_id, type_id, .. result_id, type_id, ..
} if result_id == target_id && type_id == struct_id => { } if result_id == target_id && type_id == struct_id => {
return Some(params[0]); return Some(params[0]);
}, },
parse::Instruction::TypeRuntimeArray { result_id, type_id } Instruction::TypeRuntimeArray { result_id, type_id }
if result_id == target_id && type_id == struct_id => { if result_id == target_id && type_id == struct_id => {
return Some(params[0]); return Some(params[0]);
}, },
@ -186,58 +167,63 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
let diff = req_size.checked_sub(cur_size as u32).unwrap(); let diff = req_size.checked_sub(cur_size as u32).unwrap();
if diff >= 1 { if diff >= 1 {
rust_members.push(Member { rust_members.push(Member {
name: format!("_dummy{}", next_padding_num), name: Ident::new(&format!("_dummy{}", next_padding_num), Span::call_site()),
value: format!("[u8; {}]", diff), ty: quote!{ [u8; {}] },
offset: None, });
});
} }
} }
// We can only implement Clone if there's no unsized member in the struct. // We can only implement Clone if there's no unsized member in the struct.
let (impl_text, derive_text) = if current_rust_offset.is_some() { let (clone_impl, copy_derive) = if current_rust_offset.is_some() {
let i = format!("\nimpl Clone for {name} {{\n fn clone(&self) -> Self {{\n \ let mut copies = vec!();
{name} {{\n{copies}\n }}\n }}\n}}\n", for member in &rust_members {
name = name, let name = &member.name;
copies = rust_members copies.push(quote!{ #name: self.#name, });
.iter() }
.map(Member::copy_text) (
.collect::<Vec<_>>() // Clone is implemented manually because members can be large arrays
.join(",\n")); // that do not implement Clone, but do implement Copy
(i, "#[derive(Copy)]") quote!{
impl Clone for #name {
fn clone(&self) -> Self {
#name {
#( #copies )*
}
}
}
},
quote!{ #[derive(Copy)] }
)
} else { } else {
("".to_owned(), "") (quote!{}, quote!{})
}; };
let s = let mut members = vec!();
format!("#[repr(C)]\n{derive_text}\n#[allow(non_snake_case)]\npub struct {name} \ for member in &rust_members {
{{\n{members}\n}} /* total_size: {t:?} */\n{impl_text}", let name = &member.name;
name = name, let ty = &member.ty;
members = rust_members members.push(quote!(pub #name: #ty,));
.iter() }
.map(Member::declaration_text)
.collect::<Vec<_>>() let ast = quote! {
.join(",\n"), #[repr(C)]
t = spirv_req_total_size, #copy_derive
impl_text = impl_text, #[allow(non_snake_case)]
derive_text = derive_text); pub struct #name {
(s, #( #members )*
spirv_req_total_size }
.map(|sz| sz as usize) #clone_impl
.or(current_rust_offset)) };
(ast, spirv_req_total_size.map(|sz| sz as usize).or(current_rust_offset))
} }
/// Returns true if a `BuiltIn` decorator is applied on a struct member. /// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool { fn is_builtin_member(doc: &Spirv, id: u32, member_id: u32) -> bool {
for instruction in &doc.instructions { for instruction in &doc.instructions {
match *instruction { match *instruction {
parse::Instruction::MemberDecorate { Instruction::MemberDecorate { target_id, member, decoration: Decoration::DecorationBuiltIn, .. }
target_id, if target_id == id && member == member_id => { return true }
member,
decoration: enums::Decoration::DecorationBuiltIn,
..
} if target_id == id && member == member_id => {
return true;
},
_ => (), _ => (),
} }
} }
@ -248,17 +234,13 @@ fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
/// Returns the type name to put in the Rust struct, and its size and alignment. /// Returns the type name to put in the Rust struct, and its size and alignment.
/// ///
/// The size can be `None` if it's only known at runtime. /// The size can be `None` if it's only known at runtime.
pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) { pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>, usize) {
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
match instruction { match instruction {
&parse::Instruction::TypeBool { result_id } if result_id == searched => { &Instruction::TypeBool { result_id } if result_id == searched => {
panic!("Can't put booleans in structs") panic!("Can't put booleans in structs")
}, }
&parse::Instruction::TypeInt { &Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
result_id,
width,
signedness,
} if result_id == searched => {
match (width, signedness) { match (width, signedness) {
(8, true) => { (8, true) => {
#[repr(C)] #[repr(C)]
@ -267,7 +249,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("i8".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{i8}, Some(size), mem::align_of::<Foo>());
}, },
(8, false) => { (8, false) => {
#[repr(C)] #[repr(C)]
@ -276,7 +258,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("u8".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{u8}, Some(size), mem::align_of::<Foo>());
}, },
(16, true) => { (16, true) => {
#[repr(C)] #[repr(C)]
@ -285,7 +267,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("i16".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{i16}, Some(size), mem::align_of::<Foo>());
}, },
(16, false) => { (16, false) => {
#[repr(C)] #[repr(C)]
@ -294,7 +276,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("u16".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{u16}, Some(size), mem::align_of::<Foo>());
}, },
(32, true) => { (32, true) => {
#[repr(C)] #[repr(C)]
@ -303,7 +285,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("i32".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{i32}, Some(size), mem::align_of::<Foo>());
}, },
(32, false) => { (32, false) => {
#[repr(C)] #[repr(C)]
@ -312,7 +294,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("u32".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{u32}, Some(size), mem::align_of::<Foo>());
}, },
(64, true) => { (64, true) => {
#[repr(C)] #[repr(C)]
@ -321,7 +303,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("i64".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{i64}, Some(size), mem::align_of::<Foo>());
}, },
(64, false) => { (64, false) => {
#[repr(C)] #[repr(C)]
@ -330,12 +312,12 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("u64".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{u64}, Some(size), mem::align_of::<Foo>());
}, },
_ => panic!("No Rust equivalent for an integer of width {}", width), _ => panic!("No Rust equivalent for an integer of width {}", width),
} }
}, }
&parse::Instruction::TypeFloat { result_id, width } if result_id == searched => { &Instruction::TypeFloat { result_id, width } if result_id == searched => {
match width { match width {
32 => { 32 => {
#[repr(C)] #[repr(C)]
@ -344,7 +326,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("f32".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{f32}, Some(size), mem::align_of::<Foo>());
}, },
64 => { 64 => {
#[repr(C)] #[repr(C)]
@ -353,61 +335,58 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
after: u8, after: u8,
} }
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize }; let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
return ("f64".to_owned(), Some(size), mem::align_of::<Foo>()); return (quote!{f64}, Some(size), mem::align_of::<Foo>());
}, },
_ => panic!("No Rust equivalent for a floating-point of width {}", width), _ => panic!("No Rust equivalent for a floating-point of width {}", width),
} }
}, }
&parse::Instruction::TypeVector { &Instruction::TypeVector {
result_id, result_id,
component_id, component_id,
count, count,
} if result_id == searched => { } if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>()); debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, t_size, t_align) = type_from_id(doc, component_id); let (ty, t_size, t_align) = type_from_id(doc, component_id);
return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align); let array_length = count as usize;
}, let size = t_size.map(|s| s * count as usize);
&parse::Instruction::TypeMatrix { return (quote!{ [#ty; #array_length] }, size, t_align);
}
&Instruction::TypeMatrix {
result_id, result_id,
column_type_id, column_type_id,
column_count, column_count,
} if result_id == searched => { } if result_id == searched => {
// FIXME: row-major or column-major // FIXME: row-major or column-major
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>()); debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, t_size, t_align) = type_from_id(doc, column_type_id); let (ty, t_size, t_align) = type_from_id(doc, column_type_id);
return (format!("[{}; {}]", t, column_count), let array_length = column_count as usize;
t_size.map(|s| s * column_count as usize), let size = t_size.map(|s| s * column_count as usize);
t_align); return (quote!{ [#ty; #array_length] }, size, t_align);
}, }
&parse::Instruction::TypeArray { &Instruction::TypeArray {
result_id, result_id,
type_id, type_id,
length_id, length_id,
} if result_id == searched => { } if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>()); debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, t_size, t_align) = type_from_id(doc, type_id); let (ty, t_size, t_align) = type_from_id(doc, type_id);
let t_size = t_size.expect("array components must be sized"); let t_size = t_size.expect("array components must be sized");
let len = doc.instructions let len = doc.instructions
.iter() .iter()
.filter_map(|e| match e { .filter_map(|e| match e {
&parse::Instruction::Constant { &Instruction::Constant { result_id, ref data, .. }
result_id, if result_id == length_id => Some(data.clone()),
ref data, _ => None,
.. })
} if result_id == length_id => Some(data.clone()),
_ => None,
})
.next() .next()
.expect("failed to find array length"); .expect("failed to find array length");
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64); let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
let stride = doc.instructions.iter().filter_map(|e| match e { let stride = doc.instructions.iter().filter_map(|e| match e {
parse::Instruction::Decorate{ Instruction::Decorate {
target_id, target_id,
decoration: enums::Decoration::DecorationArrayStride, decoration: Decoration::DecorationArrayStride,
ref params, ref params,
} if *target_id == searched => { } if *target_id == searched => Some(params[0]),
Some(params[0])
},
_ => None, _ => None,
}) })
.next().expect("failed to find ArrayStride decoration"); .next().expect("failed to find ArrayStride decoration");
@ -417,27 +396,30 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
the array element in a struct or rounding up the size of a vector or matrix \ the array element in a struct or rounding up the size of a vector or matrix \
(e.g. increase a vec3 to a vec4)") (e.g. increase a vec3 to a vec4)")
} }
return (format!("[{}; {}]", t, len), Some(t_size * len as usize), t_align); let array_length = len as usize;
}, let size = Some(t_size * len as usize);
&parse::Instruction::TypeRuntimeArray { result_id, type_id } return (quote!{ [#ty; #array_length] }, size, t_align);
}
&Instruction::TypeRuntimeArray { result_id, type_id }
if result_id == searched => { if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>()); debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, _, t_align) = type_from_id(doc, type_id); let (ty, _, t_align) = type_from_id(doc, type_id);
return (format!("[{}]", t), None, t_align); return (quote!{ [#ty] }, None, t_align);
}, }
&parse::Instruction::TypeStruct { &Instruction::TypeStruct {
result_id, result_id,
ref member_types, ref member_types,
} if result_id == searched => { } if result_id == searched => {
// TODO: take the Offset member decorate into account? // TODO: take the Offset member decorate into account?
let name = ::name_from_id(doc, result_id); let name = Ident::new(&spirv_search::name_from_id(doc, result_id), Span::call_site());
let ty = quote!{ #name };
let (_, size) = write_struct(doc, result_id, member_types); let (_, size) = write_struct(doc, result_id, member_types);
let align = member_types let align = member_types
.iter() .iter()
.map(|&t| type_from_id(doc, t).2) .map(|&t| type_from_id(doc, t).2)
.max() .max()
.unwrap_or(1); .unwrap_or(1);
return (name, size, align); return (ty, size, align);
}, },
_ => (), _ => (),
} }