mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 22:34:43 +00:00
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:
parent
b6c4d84e61
commit
d779829cbd
@ -1,5 +1,6 @@
|
||||
# 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.
|
||||
- Remove vulkano_shaders::build_glsl_shaders
|
||||
- Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage`
|
||||
|
@ -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
|
||||
// reached that point in the execution.
|
||||
.then_signal_fence_and_flush().unwrap();
|
||||
|
||||
|
||||
// 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
|
||||
// available if we didn't call `.then_signal_fence_and_flush()` earlier.
|
||||
|
102
examples/src/bin/specialization-constants.rs
Normal file
102
examples/src/bin/specialization-constants.rs
Normal 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");
|
||||
}
|
@ -14,6 +14,8 @@ proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.15"
|
||||
quote = "0.6"
|
||||
proc-macro2 = "0.4"
|
||||
vulkano-shaders = { version = "0.10", path = "../vulkano-shaders" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -156,6 +156,8 @@
|
||||
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
extern crate quote;
|
||||
extern crate syn;
|
||||
extern crate vulkano_shaders;
|
||||
|
||||
@ -164,15 +166,13 @@ use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
enum SourceKind {
|
||||
Src(String),
|
||||
Path(String),
|
||||
}
|
||||
|
||||
#[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 source_code = {
|
||||
@ -244,8 +244,5 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
let content = vulkano_shaders::compile(&source_code, ty).unwrap();
|
||||
|
||||
vulkano_shaders::reflect("Shader", content.as_binary())
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap()
|
||||
vulkano_shaders::reflect("Shader", content.as_binary()).unwrap().into()
|
||||
}
|
||||
|
@ -10,3 +10,6 @@ categories = ["rendering::graphics-api"]
|
||||
|
||||
[dependencies]
|
||||
shaderc = "0.3"
|
||||
syn = "0.15"
|
||||
quote = "0.6"
|
||||
proc-macro2 = "0.4"
|
||||
|
@ -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);
|
||||
}
|
@ -9,10 +9,13 @@
|
||||
|
||||
use std::cmp;
|
||||
|
||||
use enums;
|
||||
use parse;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
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
|
||||
|
||||
// Finding all the descriptors.
|
||||
@ -20,42 +23,35 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
|
||||
struct Descriptor {
|
||||
set: u32,
|
||||
binding: u32,
|
||||
desc_ty: String,
|
||||
desc_ty: TokenStream,
|
||||
array_count: u64,
|
||||
readonly: bool,
|
||||
}
|
||||
|
||||
// Looping to find all the elements that have the `DescriptorSet` decoration.
|
||||
for instruction in doc.instructions.iter() {
|
||||
let (variable_id, descriptor_set) = match instruction {
|
||||
&parse::Instruction::Decorate {
|
||||
target_id,
|
||||
decoration: enums::Decoration::DecorationDescriptorSet,
|
||||
ref params,
|
||||
} => {
|
||||
(target_id, params[0])
|
||||
},
|
||||
let (variable_id, set) = match instruction {
|
||||
&Instruction::Decorate { target_id, decoration: Decoration::DecorationDescriptorSet, ref params }
|
||||
=> (target_id, params[0]),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
// Find which type is pointed to by this variable.
|
||||
let pointed_ty = pointer_variable_ty(doc, variable_id);
|
||||
// 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.
|
||||
let binding = doc.instructions
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
match i {
|
||||
&parse::Instruction::Decorate {
|
||||
&Instruction::Decorate {
|
||||
target_id,
|
||||
decoration: enums::Decoration::DecorationBinding,
|
||||
decoration: Decoration::DecorationBinding,
|
||||
ref params,
|
||||
} if target_id == variable_id => {
|
||||
Some(params[0])
|
||||
},
|
||||
_ => None, // TODO: other types
|
||||
} if target_id == variable_id => Some(params[0]),
|
||||
_ => None, // TODO: other types
|
||||
}
|
||||
})
|
||||
.next()
|
||||
@ -63,31 +59,20 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
|
||||
|
||||
// Find information about the kind of binding for this descriptor.
|
||||
let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
|
||||
.expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe \
|
||||
unimplemented)",
|
||||
name,
|
||||
pointed_ty));
|
||||
|
||||
descriptors.push(Descriptor {
|
||||
desc_ty: desc_ty,
|
||||
set: descriptor_set,
|
||||
binding: binding,
|
||||
array_count: array_count,
|
||||
readonly: readonly,
|
||||
});
|
||||
.expect(&format!(
|
||||
"Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)",
|
||||
name,
|
||||
pointed_ty
|
||||
));
|
||||
descriptors.push(Descriptor { desc_ty, set, binding, array_count, readonly });
|
||||
}
|
||||
|
||||
// Looping to find all the push constant structs.
|
||||
let mut push_constants_size = 0;
|
||||
for instruction in doc.instructions.iter() {
|
||||
let type_id = match instruction {
|
||||
&parse::Instruction::TypePointer {
|
||||
type_id,
|
||||
storage_class: enums::StorageClass::StorageClassPushConstant,
|
||||
..
|
||||
} => {
|
||||
type_id
|
||||
},
|
||||
&Instruction::TypePointer { type_id, storage_class: StorageClass::StorageClassPushConstant, .. }
|
||||
=> type_id,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
@ -100,127 +85,106 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
|
||||
let descriptor_body = descriptors
|
||||
.iter()
|
||||
.map(|d| {
|
||||
format!(
|
||||
"({set}, {binding}) => Some(DescriptorDesc {{
|
||||
ty: {desc_ty},
|
||||
array_count: {array_count},
|
||||
stages: self.0.clone(),
|
||||
readonly: {readonly},
|
||||
}}),",
|
||||
set = d.set,
|
||||
binding = d.binding,
|
||||
desc_ty = d.desc_ty,
|
||||
array_count = d.array_count,
|
||||
readonly = if d.readonly { "true" } else { "false" }
|
||||
)
|
||||
|
||||
let set = d.set as usize;
|
||||
let binding = d.binding as usize;
|
||||
let desc_ty = &d.desc_ty;
|
||||
let array_count = d.array_count as u32;
|
||||
let readonly = d.readonly;
|
||||
quote!{
|
||||
(#set, #binding) => Some(DescriptorDesc {
|
||||
ty: #desc_ty,
|
||||
array_count: #array_count,
|
||||
stages: self.0.clone(),
|
||||
readonly: #readonly,
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.concat();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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.
|
||||
let num_bindings_in_set_body = {
|
||||
(0 .. num_sets)
|
||||
.map(|set| {
|
||||
let num = descriptors
|
||||
.iter()
|
||||
.filter(|d| d.set == set)
|
||||
.fold(0, |s, d| cmp::max(s, 1 + d.binding));
|
||||
format!("{set} => Some({num}),", set = set, num = num)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.concat()
|
||||
};
|
||||
let num_bindings_in_set_body = (0 .. num_sets)
|
||||
.map(|set| {
|
||||
let num = descriptors
|
||||
.iter()
|
||||
.filter(|d| d.set == set as u32)
|
||||
.fold(0, |s, d| cmp::max(s, 1 + d.binding)) as usize;
|
||||
quote!{ #set => Some(#num), }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Writing the body of the `num_push_constants_ranges` method.
|
||||
let num_push_constants_ranges_body = {
|
||||
if push_constants_size == 0 { "0" } else { "1" }
|
||||
};
|
||||
let num_push_constants_ranges_body = if push_constants_size == 0 { 0 } else { 1 } as usize;
|
||||
|
||||
// Writing the body of the `push_constants_range` method.
|
||||
let push_constants_range_body = format!(
|
||||
r#"
|
||||
if num != 0 || {pc_size} == 0 {{ return None; }}
|
||||
Some(PipelineLayoutDescPcRange {{
|
||||
offset: 0, // FIXME: not necessarily true
|
||||
size: {pc_size},
|
||||
stages: ShaderStages::all(), // FIXME: wrong
|
||||
}})
|
||||
"#,
|
||||
pc_size = push_constants_size
|
||||
let push_constants_range_body = quote!(
|
||||
if num != 0 || #push_constants_size == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(PipelineLayoutDescPcRange {
|
||||
offset: 0, // FIXME: not necessarily true
|
||||
size: #push_constants_size,
|
||||
stages: ShaderStages::all(), // FIXME: wrong
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
format!(
|
||||
r#"
|
||||
quote!{
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Layout(pub ShaderStages);
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl PipelineLayoutDesc for Layout {{
|
||||
fn num_sets(&self) -> usize {{
|
||||
{num_sets}
|
||||
}}
|
||||
unsafe impl PipelineLayoutDesc for Layout {
|
||||
fn num_sets(&self) -> usize {
|
||||
#num_sets
|
||||
}
|
||||
|
||||
fn num_bindings_in_set(&self, set: usize) -> Option<usize> {{
|
||||
match set {{
|
||||
{num_bindings_in_set_body}
|
||||
fn num_bindings_in_set(&self, set: usize) -> Option<usize> {
|
||||
match set {
|
||||
#( #num_bindings_in_set_body )*
|
||||
_ => None
|
||||
}}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {{
|
||||
match (set, binding) {{
|
||||
{descriptor_body}
|
||||
fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {
|
||||
match (set, binding) {
|
||||
#( #descriptor_body )*
|
||||
_ => None
|
||||
}}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
fn num_push_constants_ranges(&self) -> usize {{
|
||||
{num_push_constants_ranges_body}
|
||||
}}
|
||||
fn num_push_constants_ranges(&self) -> usize {
|
||||
#num_push_constants_ranges_body
|
||||
}
|
||||
|
||||
fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {{
|
||||
{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
|
||||
)
|
||||
fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {
|
||||
#push_constants_range_body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
|
||||
/// 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
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
&parse::Instruction::Variable {
|
||||
result_type_id,
|
||||
result_id,
|
||||
..
|
||||
} if result_id == variable => {
|
||||
Some(result_type_id)
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
&Instruction::Variable { result_type_id, result_id, .. }
|
||||
if result_id == variable => Some(result_type_id),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
doc.instructions
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
&parse::Instruction::TypePointer { result_id, type_id, .. }
|
||||
if result_id == var_ty => {
|
||||
Some(type_id)
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
&Instruction::TypePointer { result_id, type_id, .. }
|
||||
if result_id == var_ty => Some(type_id),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.unwrap()
|
||||
}
|
||||
@ -229,26 +193,21 @@ fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
|
||||
/// read-only, and the number of array elements.
|
||||
///
|
||||
/// 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)
|
||||
-> Option<(String, bool, u64)> {
|
||||
fn descriptor_infos(doc: &Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
|
||||
-> Option<(TokenStream, bool, u64)>
|
||||
{
|
||||
doc.instructions.iter().filter_map(|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.
|
||||
let is_ssbo = doc.instructions.iter().filter_map(|i| {
|
||||
match i {
|
||||
&parse::Instruction::Decorate
|
||||
{ target_id, decoration: enums::Decoration::DecorationBufferBlock, .. }
|
||||
if target_id == pointed_ty =>
|
||||
{
|
||||
Some(true)
|
||||
},
|
||||
&parse::Instruction::Decorate
|
||||
{ target_id, decoration: enums::Decoration::DecorationBlock, .. }
|
||||
if target_id == pointed_ty =>
|
||||
{
|
||||
Some(false)
|
||||
},
|
||||
&Instruction::Decorate
|
||||
{ target_id, decoration: Decoration::DecorationBufferBlock, .. }
|
||||
if target_id == pointed_ty => Some(true),
|
||||
&Instruction::Decorate
|
||||
{ target_id, decoration: Decoration::DecorationBlock, .. }
|
||||
if target_id == pointed_ty => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}).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.
|
||||
//let non_writable = false; // TODO: tricky because the decoration is on struct members
|
||||
|
||||
let desc = format!("DescriptorDescTy::Buffer(DescriptorBufferDesc {{
|
||||
dynamic: Some(false),
|
||||
storage: {},
|
||||
}})", if is_ssbo { "true" } else { "false "});
|
||||
let desc = quote!{
|
||||
DescriptorDescTy::Buffer(DescriptorBufferDesc {
|
||||
dynamic: Some(false),
|
||||
storage: #is_ssbo,
|
||||
})
|
||||
};
|
||||
|
||||
Some((desc, true, 1))
|
||||
},
|
||||
|
||||
&parse::Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled,
|
||||
ref format, .. } if result_id == pointed_ty =>
|
||||
}
|
||||
&Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled, ref format, .. }
|
||||
if result_id == pointed_ty =>
|
||||
{
|
||||
let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
|
||||
have a Sampled operand of 1 or 2");
|
||||
|
||||
let ms = if ms { "true" } else { "false" };
|
||||
let arrayed = if arrayed {
|
||||
"DescriptorImageDescArray::Arrayed { max_layers: None }"
|
||||
} else {
|
||||
"DescriptorImageDescArray::NonArrayed"
|
||||
let arrayed = match arrayed {
|
||||
true => quote!{ DescriptorImageDescArray::Arrayed { max_layers: None } },
|
||||
false => quote!{ DescriptorImageDescArray::NonArrayed }
|
||||
};
|
||||
|
||||
if let &enums::Dim::DimSubpassData = dim {
|
||||
// We are an input attachment.
|
||||
assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
|
||||
an OpTypeImage whose dimension is \
|
||||
SubpassData");
|
||||
assert!(if let &enums::ImageFormat::ImageFormatUnknown = format { true }
|
||||
else { false }, "If Dim is SubpassData, Image Format must be Unknown");
|
||||
assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");
|
||||
match dim {
|
||||
Dim::DimSubpassData => {
|
||||
// We are an input attachment.
|
||||
assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
|
||||
an OpTypeImage whose dimension is \
|
||||
SubpassData");
|
||||
assert!(if let &ImageFormat::ImageFormatUnknown = format { true }
|
||||
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 {{
|
||||
multisampled: {},
|
||||
array_layers: {}
|
||||
}}", ms, arrayed);
|
||||
let desc = quote!{
|
||||
DescriptorDescTy::InputAttachment {
|
||||
multisampled: #ms,
|
||||
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 {
|
||||
// We are a texel buffer.
|
||||
let desc = format!("DescriptorDescTy::TexelBuffer {{
|
||||
storage: {},
|
||||
format: None, // TODO: specify format if known
|
||||
}}", !sampled);
|
||||
Some((desc, true, 1))
|
||||
}
|
||||
_ => {
|
||||
// We are a sampled or storage image.
|
||||
let ty = match force_combined_image_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 {
|
||||
// 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))
|
||||
Some((desc, true, 1))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
&parse::Instruction::TypeSampledImage { result_id, image_type_id }
|
||||
if result_id == pointed_ty =>
|
||||
{
|
||||
descriptor_infos(doc, image_type_id, true)
|
||||
},
|
||||
&Instruction::TypeSampledImage { result_id, image_type_id } if result_id == pointed_ty
|
||||
=> descriptor_infos(doc, image_type_id, true),
|
||||
|
||||
&parse::Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
|
||||
let desc = format!("DescriptorDescTy::Sampler");
|
||||
&Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
|
||||
let desc = quote!{ DescriptorDescTy::Sampler };
|
||||
Some((desc, true, 1))
|
||||
},
|
||||
|
||||
&parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
|
||||
}
|
||||
&Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
|
||||
let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
|
||||
None => return None,
|
||||
Some(v) => v,
|
||||
};
|
||||
assert_eq!(arr, 1); // TODO: implement?
|
||||
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");
|
||||
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))
|
||||
},
|
||||
|
||||
_ => None, // TODO: other types
|
||||
}
|
||||
_ => None, // TODO: other types
|
||||
}
|
||||
}).next()
|
||||
}
|
||||
|
@ -7,25 +7,17 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use enums;
|
||||
use parse;
|
||||
use syn::Ident;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use format_from_id;
|
||||
use is_builtin;
|
||||
use location_decoration;
|
||||
use name_from_id;
|
||||
use enums::{StorageClass, ExecutionModel, ExecutionMode};
|
||||
use parse::{Instruction, Spirv};
|
||||
use spirv_search;
|
||||
|
||||
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 {
|
||||
&parse::Instruction::EntryPoint {
|
||||
ref execution,
|
||||
id,
|
||||
ref name,
|
||||
ref interface,
|
||||
..
|
||||
} => {
|
||||
(execution, id, name, interface)
|
||||
},
|
||||
&Instruction::EntryPoint { ref execution, id, ref name, ref interface, .. } =>
|
||||
(execution, id, name, interface),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@ -36,230 +28,235 @@ pub fn write_entry_point(doc: &parse::Spirv, instruction: &parse::Instruction) -
|
||||
.chain(ep_name.chars().skip(1))
|
||||
.collect();
|
||||
|
||||
let interface_structs =
|
||||
write_interface_structs(doc,
|
||||
&capitalized_ep_name,
|
||||
interface,
|
||||
match *execution {
|
||||
enums::ExecutionModel::ExecutionModelTessellationControl =>
|
||||
true,
|
||||
enums::ExecutionModel::ExecutionModelTessellationEvaluation =>
|
||||
true,
|
||||
enums::ExecutionModel::ExecutionModelGeometry => true,
|
||||
_ => false,
|
||||
},
|
||||
match *execution {
|
||||
enums::ExecutionModel::ExecutionModelTessellationControl =>
|
||||
true,
|
||||
_ => false,
|
||||
});
|
||||
let ignore_first_array_in = match *execution {
|
||||
ExecutionModel::ExecutionModelTessellationControl => true,
|
||||
ExecutionModel::ExecutionModelTessellationEvaluation => true,
|
||||
ExecutionModel::ExecutionModelGeometry => true,
|
||||
_ => false,
|
||||
};
|
||||
let ignore_first_array_out = match *execution {
|
||||
ExecutionModel::ExecutionModelTessellationControl => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let interface_structs = write_interface_structs(
|
||||
doc,
|
||||
&capitalized_ep_name,
|
||||
interface,
|
||||
ignore_first_array_in,
|
||||
ignore_first_array_out
|
||||
);
|
||||
|
||||
let spec_consts_struct = if ::spec_consts::has_specialization_constants(doc) {
|
||||
"SpecializationConstants"
|
||||
quote!{ SpecializationConstants }
|
||||
} else {
|
||||
"()"
|
||||
quote!{ () }
|
||||
};
|
||||
|
||||
let (ty, f_call) = {
|
||||
if let enums::ExecutionModel::ExecutionModelGLCompute = *execution {
|
||||
(format!("::vulkano::pipeline::shader::ComputeEntryPoint<{}, Layout>",
|
||||
spec_consts_struct),
|
||||
format!("compute_entry_point(::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _), \
|
||||
Layout(ShaderStages {{ compute: true, .. ShaderStages::none() }}))"))
|
||||
|
||||
if let ExecutionModel::ExecutionModelGLCompute = *execution {
|
||||
(
|
||||
quote!{ ::vulkano::pipeline::shader::ComputeEntryPoint<#spec_consts_struct, Layout> },
|
||||
quote!{ compute_entry_point(
|
||||
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
|
||||
Layout(ShaderStages { compute: true, .. ShaderStages::none() })
|
||||
)}
|
||||
)
|
||||
} else {
|
||||
let ty = match *execution {
|
||||
enums::ExecutionModel::ExecutionModelVertex => {
|
||||
"::vulkano::pipeline::shader::GraphicsShaderType::Vertex".to_owned()
|
||||
},
|
||||
let entry_ty = match *execution {
|
||||
ExecutionModel::ExecutionModelVertex =>
|
||||
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::Vertex },
|
||||
|
||||
enums::ExecutionModel::ExecutionModelTessellationControl => {
|
||||
"::vulkano::pipeline::shader::GraphicsShaderType::TessellationControl"
|
||||
.to_owned()
|
||||
},
|
||||
ExecutionModel::ExecutionModelTessellationControl =>
|
||||
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::TessellationControl },
|
||||
|
||||
enums::ExecutionModel::ExecutionModelTessellationEvaluation => {
|
||||
"::vulkano::pipeline::shader::GraphicsShaderType::TessellationEvaluation"
|
||||
.to_owned()
|
||||
},
|
||||
ExecutionModel::ExecutionModelTessellationEvaluation =>
|
||||
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::TessellationEvaluation },
|
||||
|
||||
enums::ExecutionModel::ExecutionModelGeometry => {
|
||||
ExecutionModel::ExecutionModelGeometry => {
|
||||
let mut execution_mode = None;
|
||||
|
||||
for instruction in doc.instructions.iter() {
|
||||
if let &parse::Instruction::ExecutionMode {
|
||||
target_id,
|
||||
ref mode,
|
||||
..
|
||||
} = instruction
|
||||
{
|
||||
if target_id != id {
|
||||
continue;
|
||||
if let &Instruction::ExecutionMode { target_id, ref mode, .. } = instruction {
|
||||
if target_id == id {
|
||||
execution_mode = match mode {
|
||||
&ExecutionMode::ExecutionModeInputPoints => Some(quote!{ Points }),
|
||||
&ExecutionMode::ExecutionModeInputLines => Some(quote!{ Lines }),
|
||||
&ExecutionMode::ExecutionModeInputLinesAdjacency =>
|
||||
Some(quote!{ LinesWithAdjacency }),
|
||||
&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!(
|
||||
"::vulkano::pipeline::shader::GraphicsShaderType::Geometry(
|
||||
\
|
||||
::vulkano::pipeline::shader::GeometryShaderExecutionMode::{0}
|
||||
\
|
||||
)",
|
||||
execution_mode.unwrap()
|
||||
)
|
||||
},
|
||||
quote!{
|
||||
::vulkano::pipeline::shader::GraphicsShaderType::Geometry(
|
||||
::vulkano::pipeline::shader::GeometryShaderExecutionMode::#execution_mode
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enums::ExecutionModel::ExecutionModelFragment => {
|
||||
"::vulkano::pipeline::shader::GraphicsShaderType::Fragment".to_owned()
|
||||
},
|
||||
ExecutionModel::ExecutionModelFragment =>
|
||||
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::Fragment },
|
||||
|
||||
enums::ExecutionModel::ExecutionModelGLCompute => {
|
||||
unreachable!()
|
||||
},
|
||||
ExecutionModel::ExecutionModelGLCompute => unreachable!(),
|
||||
|
||||
enums::ExecutionModel::ExecutionModelKernel => panic!("Kernels are not supported"),
|
||||
ExecutionModel::ExecutionModelKernel => panic!("Kernels are not supported"),
|
||||
};
|
||||
|
||||
let stage = match *execution {
|
||||
enums::ExecutionModel::ExecutionModelVertex => {
|
||||
"ShaderStages { vertex: true, .. ShaderStages::none() }"
|
||||
},
|
||||
enums::ExecutionModel::ExecutionModelTessellationControl => {
|
||||
"ShaderStages { tessellation_control: true, .. ShaderStages::none() }"
|
||||
},
|
||||
enums::ExecutionModel::ExecutionModelTessellationEvaluation => {
|
||||
"ShaderStages { tessellation_evaluation: true, .. ShaderStages::none() }"
|
||||
},
|
||||
enums::ExecutionModel::ExecutionModelGeometry => {
|
||||
"ShaderStages { geometry: true, .. ShaderStages::none() }"
|
||||
},
|
||||
enums::ExecutionModel::ExecutionModelFragment => {
|
||||
"ShaderStages { fragment: true, .. ShaderStages::none() }"
|
||||
},
|
||||
enums::ExecutionModel::ExecutionModelGLCompute => unreachable!(),
|
||||
enums::ExecutionModel::ExecutionModelKernel => unreachable!(),
|
||||
ExecutionModel::ExecutionModelVertex =>
|
||||
quote!{ ShaderStages { vertex: true, .. ShaderStages::none() } },
|
||||
|
||||
ExecutionModel::ExecutionModelTessellationControl =>
|
||||
quote!{ ShaderStages { tessellation_control: true, .. ShaderStages::none() } },
|
||||
|
||||
ExecutionModel::ExecutionModelTessellationEvaluation =>
|
||||
quote!{ ShaderStages { tessellation_evaluation: true, .. ShaderStages::none() } },
|
||||
|
||||
ExecutionModel::ExecutionModelGeometry =>
|
||||
quote!{ ShaderStages { geometry: true, .. ShaderStages::none() } },
|
||||
|
||||
ExecutionModel::ExecutionModelFragment =>
|
||||
quote!{ ShaderStages { fragment: true, .. ShaderStages::none() } },
|
||||
|
||||
ExecutionModel::ExecutionModelGLCompute => unreachable!(),
|
||||
ExecutionModel::ExecutionModelKernel => unreachable!(),
|
||||
};
|
||||
|
||||
let t = format!("::vulkano::pipeline::shader::GraphicsEntryPoint<{0}, {1}Input, \
|
||||
{1}Output, Layout>",
|
||||
spec_consts_struct,
|
||||
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);
|
||||
let mut capitalized_ep_name_input = capitalized_ep_name.clone();
|
||||
capitalized_ep_name_input.push_str("Input");
|
||||
let capitalized_ep_name_input = Ident::new(&capitalized_ep_name_input, Span::call_site());
|
||||
|
||||
(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!(
|
||||
r#"
|
||||
/// Returns a logical struct describing the entry point named `{ep_name}`.
|
||||
#[inline]
|
||||
#[allow(unsafe_code)]
|
||||
pub fn {ep_name}_entry_point(&self) -> {ty} {{
|
||||
unsafe {{
|
||||
#[allow(dead_code)]
|
||||
static NAME: [u8; {ep_name_lenp1}] = [{encoded_ep_name}, 0]; // "{ep_name}"
|
||||
self.shader.{f_call}
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
ep_name = ep_name,
|
||||
ep_name_lenp1 = ep_name.chars().count() + 1,
|
||||
ty = ty,
|
||||
encoded_ep_name = ep_name
|
||||
.chars()
|
||||
.map(|c| (c as u32).to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
f_call = f_call
|
||||
);
|
||||
let mut method_name = ep_name.clone();
|
||||
method_name.push_str("_entry_point");
|
||||
let method_ident = Ident::new(&method_name, Span::call_site());
|
||||
|
||||
let ep_name_lenp1 = ep_name.chars().count() + 1;
|
||||
let encoded_ep_name = ep_name.chars().map(|c| (c as u8)).collect::<Vec<_>>();
|
||||
|
||||
let entry_point = quote!{
|
||||
/// Returns a logical struct describing the entry point named `{ep_name}`.
|
||||
#[inline]
|
||||
#[allow(unsafe_code)]
|
||||
pub fn #method_ident(&self) -> #ty {
|
||||
unsafe {
|
||||
#[allow(dead_code)]
|
||||
static NAME: [u8; #ep_name_lenp1] = [ #( #encoded_ep_name ),* , 0];
|
||||
self.shader.#f_call
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(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)
|
||||
-> String {
|
||||
let mut input_elements = Vec::new();
|
||||
let mut output_elements = Vec::new();
|
||||
-> TokenStream {
|
||||
let mut input_elements = vec!();
|
||||
let mut output_elements = vec!();
|
||||
|
||||
// Filling `input_elements` and `output_elements`.
|
||||
for interface in interface.iter() {
|
||||
for i in doc.instructions.iter() {
|
||||
match i {
|
||||
&parse::Instruction::Variable {
|
||||
&Instruction::Variable {
|
||||
result_type_id,
|
||||
result_id,
|
||||
ref storage_class,
|
||||
..
|
||||
} if &result_id == interface => {
|
||||
if is_builtin(doc, result_id) {
|
||||
if spirv_search::is_builtin(doc, result_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (to_write, ignore_first_array) = match storage_class {
|
||||
&enums::StorageClass::StorageClassInput => (&mut input_elements,
|
||||
ignore_first_array_in),
|
||||
&enums::StorageClass::StorageClassOutput => (&mut output_elements,
|
||||
ignore_first_array_out),
|
||||
&StorageClass::StorageClassInput =>
|
||||
(&mut input_elements, ignore_first_array_in),
|
||||
&StorageClass::StorageClassOutput =>
|
||||
(&mut output_elements, ignore_first_array_out),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let name = name_from_id(doc, result_id);
|
||||
let name = spirv_search::name_from_id(doc, result_id);
|
||||
if name == "__unnamed" {
|
||||
continue;
|
||||
} // FIXME: hack
|
||||
|
||||
let loc = match location_decoration(doc, result_id) {
|
||||
let location = match spirv_search::location_decoration(doc, result_id) {
|
||||
Some(l) => l,
|
||||
None => panic!("Attribute `{}` (id {}) is missing a location",
|
||||
name,
|
||||
result_id),
|
||||
};
|
||||
|
||||
to_write
|
||||
.push((loc, name, format_from_id(doc, result_type_id, ignore_first_array)));
|
||||
let (format, location_len) = spirv_search::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) +
|
||||
&write_interface_struct(&format!("{}Output", capitalized_ep_name), &output_elements)
|
||||
let input: TokenStream = write_interface_struct(&format!("{}Input", capitalized_ep_name), &input_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))])
|
||||
-> String {
|
||||
fn write_interface_struct(struct_name_str: &str, attributes: &[Element]) -> TokenStream {
|
||||
// Checking for overlapping elements.
|
||||
for (offset, &(loc, ref name, (_, loc_len))) in attributes.iter().enumerate() {
|
||||
for &(loc2, ref name2, (_, loc_len2)) in attributes.iter().skip(offset + 1) {
|
||||
if loc == loc2 || (loc < loc2 && loc + loc_len as u32 > loc2) ||
|
||||
(loc2 < loc && loc2 + loc_len2 as u32 > loc)
|
||||
for (offset, element1) in attributes.iter().enumerate() {
|
||||
for element2 in attributes.iter().skip(offset + 1) {
|
||||
if element1.location == element2.location ||
|
||||
(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={}) \
|
||||
and `{}` (start={}, size={}) overlap",
|
||||
name,
|
||||
loc,
|
||||
loc_len,
|
||||
name2,
|
||||
loc2,
|
||||
loc_len2);
|
||||
and `{}` (start={}, size={}) overlap",
|
||||
element1.name,
|
||||
element1.location,
|
||||
element1.location_len,
|
||||
element2.name,
|
||||
element2.location,
|
||||
element2.location_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,73 +264,67 @@ fn write_interface_struct(struct_name: &str, attributes: &[(u32, String, (String
|
||||
let body = attributes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(num, &(loc, ref name, (ref ty, num_locs)))| {
|
||||
assert!(num_locs >= 1);
|
||||
.map(|(num, element)| {
|
||||
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!(
|
||||
"if self.num == {} {{
|
||||
self.num += 1;
|
||||
quote!{
|
||||
if self.num == #num {
|
||||
self.num += 1;
|
||||
|
||||
return Some(::vulkano::pipeline::shader::ShaderInterfaceDefEntry {{
|
||||
location: {} .. {},
|
||||
format: ::vulkano::format::Format::{},
|
||||
name: Some(::std::borrow::Cow::Borrowed(\"{}\"))
|
||||
}});
|
||||
}}",
|
||||
num,
|
||||
loc,
|
||||
loc as usize + num_locs,
|
||||
ty,
|
||||
name
|
||||
)
|
||||
return Some(::vulkano::pipeline::shader::ShaderInterfaceDefEntry {
|
||||
location: #loc .. #loc_end,
|
||||
format: ::vulkano::format::Format::#format,
|
||||
name: Some(::std::borrow::Cow::Borrowed(#name))
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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)]
|
||||
pub struct {name};
|
||||
pub struct #struct_name;
|
||||
|
||||
\
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl ::vulkano::pipeline::shader::ShaderInterfaceDef for \
|
||||
{name} {{
|
||||
type Iter = {name}Iter;
|
||||
fn elements(&self) -> {name}Iter {{
|
||||
\
|
||||
{name}Iter {{ num: 0 }}
|
||||
}}
|
||||
}}
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl ::vulkano::pipeline::shader::ShaderInterfaceDef for #struct_name {
|
||||
type Iter = #iter_name;
|
||||
fn elements(&self) -> #iter_name {
|
||||
#iter_name { num: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
\
|
||||
pub struct {name}Iter {{ num: u16 }}
|
||||
impl Iterator for {name}Iter {{
|
||||
type \
|
||||
Item = ::vulkano::pipeline::shader::ShaderInterfaceDefEntry;
|
||||
pub struct #iter_name { num: u16 }
|
||||
|
||||
impl Iterator for #iter_name {
|
||||
type Item = ::vulkano::pipeline::shader::ShaderInterfaceDefEntry;
|
||||
|
||||
#[inline]
|
||||
\
|
||||
fn next(&mut self) -> Option<Self::Item> {{
|
||||
{body}
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
#( #body )*
|
||||
None
|
||||
\
|
||||
}}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {{
|
||||
\
|
||||
let len = ({len} - self.num) as usize;
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = #len - self.num as usize;
|
||||
(len, Some(len))
|
||||
}}
|
||||
\
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for {name}Iter {{}}
|
||||
",
|
||||
name = struct_name,
|
||||
body = body,
|
||||
len = attributes.len()
|
||||
)
|
||||
impl ExactSizeIterator for #iter_name {}
|
||||
}
|
||||
}
|
||||
|
@ -7,21 +7,31 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// 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 syn::Ident;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use shaderc::{Compiler, CompileOptions};
|
||||
|
||||
pub use shaderc::{CompilationArtifact, ShaderKind};
|
||||
pub use parse::ParseError;
|
||||
|
||||
use parse::Instruction;
|
||||
use enums::Capability;
|
||||
|
||||
mod descriptor_sets;
|
||||
mod entry_point;
|
||||
mod enums;
|
||||
mod parse;
|
||||
mod spec_consts;
|
||||
mod structs;
|
||||
mod spirv_search;
|
||||
|
||||
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
|
||||
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)
|
||||
}
|
||||
|
||||
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 mut output = String::new();
|
||||
output.push_str(
|
||||
r#"
|
||||
// checking whether each required capability is enabled in the Vulkan device
|
||||
let mut cap_checks: Vec<TokenStream> = vec!();
|
||||
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)]
|
||||
use std::sync::Arc;
|
||||
#[allow(unused_imports)]
|
||||
@ -77,106 +116,51 @@ pub fn reflect(name: &str, spirv: &[u32]) -> Result<String, Error> {
|
||||
use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait;
|
||||
#[allow(unused_imports)]
|
||||
use vulkano::pipeline::shader::SpecializationMapEntry;
|
||||
"#,
|
||||
);
|
||||
|
||||
{
|
||||
// contains the data that was passed as input to this function
|
||||
let spirv_words = spirv.iter()
|
||||
.map(|&word| word.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
pub struct #struct_name {
|
||||
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
|
||||
}
|
||||
|
||||
// writing the header
|
||||
output.push_str(&format!(
|
||||
r#"
|
||||
pub struct {name} {{
|
||||
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
|
||||
}}
|
||||
impl #struct_name {
|
||||
/// Loads the shader in Vulkan as a `ShaderModule`.
|
||||
#[inline]
|
||||
#[allow(unsafe_code)]
|
||||
pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>)
|
||||
-> Result<#struct_name, ::vulkano::OomError>
|
||||
{
|
||||
#( #cap_checks )*
|
||||
let words = [ #( #spirv ),* ];
|
||||
|
||||
impl {name} {{
|
||||
/// Loads the shader in Vulkan as a `ShaderModule`.
|
||||
#[inline]
|
||||
#[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
|
||||
));
|
||||
unsafe {
|
||||
Ok(#struct_name {
|
||||
shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// follow-up of the header
|
||||
output.push_str(&format!(
|
||||
r#"
|
||||
unsafe {{
|
||||
let words = [{spirv_words}];
|
||||
|
||||
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);
|
||||
/// Returns the module that was created.
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {
|
||||
&self.shader
|
||||
}
|
||||
|
||||
#( #entry_points_inside_impl )*
|
||||
}
|
||||
|
||||
// footer
|
||||
output.push_str(&format!(
|
||||
r#"
|
||||
}}
|
||||
"#
|
||||
));
|
||||
#( #entry_points_outside_impl )*
|
||||
|
||||
output.push_str(&outside_impl);
|
||||
pub mod ty {
|
||||
#structs
|
||||
}
|
||||
|
||||
// struct definitions
|
||||
output.push_str("pub mod ty {");
|
||||
output.push_str(&structs::write_structs(&doc));
|
||||
output.push_str("}");
|
||||
#descriptor_sets
|
||||
#specialization_constants
|
||||
};
|
||||
|
||||
// descriptor sets
|
||||
output.push_str(&descriptor_sets::write_descriptor_sets(&doc));
|
||||
//println!("{}", ast.to_string());
|
||||
|
||||
// specialization constants
|
||||
output.push_str(&spec_consts::write_specialization_constants(&doc));
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
Ok(ast)
|
||||
}
|
||||
|
||||
#[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 `None` if irrelevant.
|
||||
// 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 {
|
||||
enums::Capability::CapabilityMatrix => None, // always supported
|
||||
enums::Capability::CapabilityShader => None, // always supported
|
||||
enums::Capability::CapabilityGeometry => Some("geometry_shader"),
|
||||
enums::Capability::CapabilityTessellation => Some("tessellation_shader"),
|
||||
enums::Capability::CapabilityAddresses => panic!(), // not supported
|
||||
enums::Capability::CapabilityLinkage => panic!(), // not supported
|
||||
enums::Capability::CapabilityKernel => panic!(), // not supported
|
||||
enums::Capability::CapabilityVector16 => panic!(), // not supported
|
||||
enums::Capability::CapabilityFloat16Buffer => panic!(), // not supported
|
||||
enums::Capability::CapabilityFloat16 => panic!(), // not supported
|
||||
enums::Capability::CapabilityFloat64 => Some("shader_f3264"),
|
||||
enums::Capability::CapabilityInt64 => Some("shader_int64"),
|
||||
enums::Capability::CapabilityInt64Atomics => panic!(), // not supported
|
||||
enums::Capability::CapabilityImageBasic => panic!(), // not supported
|
||||
enums::Capability::CapabilityImageReadWrite => panic!(), // not supported
|
||||
enums::Capability::CapabilityImageMipmap => panic!(), // not supported
|
||||
enums::Capability::CapabilityPipes => panic!(), // not supported
|
||||
enums::Capability::CapabilityGroups => panic!(), // not supported
|
||||
enums::Capability::CapabilityDeviceEnqueue => panic!(), // not supported
|
||||
enums::Capability::CapabilityLiteralSampler => panic!(), // not supported
|
||||
enums::Capability::CapabilityAtomicStorage => panic!(), // not supported
|
||||
enums::Capability::CapabilityInt16 => Some("shader_int16"),
|
||||
enums::Capability::CapabilityTessellationPointSize =>
|
||||
Capability::CapabilityMatrix => None, // always supported
|
||||
Capability::CapabilityShader => None, // always supported
|
||||
Capability::CapabilityGeometry => Some("geometry_shader"),
|
||||
Capability::CapabilityTessellation => Some("tessellation_shader"),
|
||||
Capability::CapabilityAddresses => panic!(), // not supported
|
||||
Capability::CapabilityLinkage => panic!(), // not supported
|
||||
Capability::CapabilityKernel => panic!(), // not supported
|
||||
Capability::CapabilityVector16 => panic!(), // not supported
|
||||
Capability::CapabilityFloat16Buffer => panic!(), // not supported
|
||||
Capability::CapabilityFloat16 => panic!(), // not supported
|
||||
Capability::CapabilityFloat64 => Some("shader_f3264"),
|
||||
Capability::CapabilityInt64 => Some("shader_int64"),
|
||||
Capability::CapabilityInt64Atomics => panic!(), // not supported
|
||||
Capability::CapabilityImageBasic => panic!(), // not supported
|
||||
Capability::CapabilityImageReadWrite => panic!(), // not supported
|
||||
Capability::CapabilityImageMipmap => panic!(), // not supported
|
||||
Capability::CapabilityPipes => panic!(), // not supported
|
||||
Capability::CapabilityGroups => panic!(), // not supported
|
||||
Capability::CapabilityDeviceEnqueue => panic!(), // not supported
|
||||
Capability::CapabilityLiteralSampler => panic!(), // not supported
|
||||
Capability::CapabilityAtomicStorage => panic!(), // not supported
|
||||
Capability::CapabilityInt16 => Some("shader_int16"),
|
||||
Capability::CapabilityTessellationPointSize =>
|
||||
Some("shader_tessellation_and_geometry_point_size"),
|
||||
enums::Capability::CapabilityGeometryPointSize =>
|
||||
Capability::CapabilityGeometryPointSize =>
|
||||
Some("shader_tessellation_and_geometry_point_size"),
|
||||
enums::Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"),
|
||||
enums::Capability::CapabilityStorageImageMultisample =>
|
||||
Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"),
|
||||
Capability::CapabilityStorageImageMultisample =>
|
||||
Some("shader_storage_image_multisample"),
|
||||
enums::Capability::CapabilityUniformBufferArrayDynamicIndexing =>
|
||||
Capability::CapabilityUniformBufferArrayDynamicIndexing =>
|
||||
Some("shader_uniform_buffer_array_dynamic_indexing"),
|
||||
enums::Capability::CapabilitySampledImageArrayDynamicIndexing =>
|
||||
Capability::CapabilitySampledImageArrayDynamicIndexing =>
|
||||
Some("shader_sampled_image_array_dynamic_indexing"),
|
||||
enums::Capability::CapabilityStorageBufferArrayDynamicIndexing =>
|
||||
Capability::CapabilityStorageBufferArrayDynamicIndexing =>
|
||||
Some("shader_storage_buffer_array_dynamic_indexing"),
|
||||
enums::Capability::CapabilityStorageImageArrayDynamicIndexing =>
|
||||
Capability::CapabilityStorageImageArrayDynamicIndexing =>
|
||||
Some("shader_storage_image_array_dynamic_indexing"),
|
||||
enums::Capability::CapabilityClipDistance => Some("shader_clip_distance"),
|
||||
enums::Capability::CapabilityCullDistance => Some("shader_cull_distance"),
|
||||
enums::Capability::CapabilityImageCubeArray => Some("image_cube_array"),
|
||||
enums::Capability::CapabilitySampleRateShading => Some("sample_rate_shading"),
|
||||
enums::Capability::CapabilityImageRect => panic!(), // not supported
|
||||
enums::Capability::CapabilitySampledRect => panic!(), // not supported
|
||||
enums::Capability::CapabilityGenericPointer => panic!(), // not supported
|
||||
enums::Capability::CapabilityInt8 => panic!(), // not supported
|
||||
enums::Capability::CapabilityInputAttachment => None, // always supported
|
||||
enums::Capability::CapabilitySparseResidency => Some("shader_resource_residency"),
|
||||
enums::Capability::CapabilityMinLod => Some("shader_resource_min_lod"),
|
||||
enums::Capability::CapabilitySampled1D => None, // always supported
|
||||
enums::Capability::CapabilityImage1D => None, // always supported
|
||||
enums::Capability::CapabilitySampledCubeArray => Some("image_cube_array"),
|
||||
enums::Capability::CapabilitySampledBuffer => None, // always supported
|
||||
enums::Capability::CapabilityImageBuffer => None, // always supported
|
||||
enums::Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"),
|
||||
enums::Capability::CapabilityStorageImageExtendedFormats =>
|
||||
Capability::CapabilityClipDistance => Some("shader_clip_distance"),
|
||||
Capability::CapabilityCullDistance => Some("shader_cull_distance"),
|
||||
Capability::CapabilityImageCubeArray => Some("image_cube_array"),
|
||||
Capability::CapabilitySampleRateShading => Some("sample_rate_shading"),
|
||||
Capability::CapabilityImageRect => panic!(), // not supported
|
||||
Capability::CapabilitySampledRect => panic!(), // not supported
|
||||
Capability::CapabilityGenericPointer => panic!(), // not supported
|
||||
Capability::CapabilityInt8 => panic!(), // not supported
|
||||
Capability::CapabilityInputAttachment => None, // always supported
|
||||
Capability::CapabilitySparseResidency => Some("shader_resource_residency"),
|
||||
Capability::CapabilityMinLod => Some("shader_resource_min_lod"),
|
||||
Capability::CapabilitySampled1D => None, // always supported
|
||||
Capability::CapabilityImage1D => None, // always supported
|
||||
Capability::CapabilitySampledCubeArray => Some("image_cube_array"),
|
||||
Capability::CapabilitySampledBuffer => None, // always supported
|
||||
Capability::CapabilityImageBuffer => None, // always supported
|
||||
Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"),
|
||||
Capability::CapabilityStorageImageExtendedFormats =>
|
||||
Some("shader_storage_image_extended_formats"),
|
||||
enums::Capability::CapabilityImageQuery => None, // always supported
|
||||
enums::Capability::CapabilityDerivativeControl => None, // always supported
|
||||
enums::Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"),
|
||||
enums::Capability::CapabilityTransformFeedback => panic!(), // not supported
|
||||
enums::Capability::CapabilityGeometryStreams => panic!(), // not supported
|
||||
enums::Capability::CapabilityStorageImageReadWithoutFormat =>
|
||||
Capability::CapabilityImageQuery => None, // always supported
|
||||
Capability::CapabilityDerivativeControl => None, // always supported
|
||||
Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"),
|
||||
Capability::CapabilityTransformFeedback => panic!(), // not supported
|
||||
Capability::CapabilityGeometryStreams => panic!(), // not supported
|
||||
Capability::CapabilityStorageImageReadWithoutFormat =>
|
||||
Some("shader_storage_image_read_without_format"),
|
||||
enums::Capability::CapabilityStorageImageWriteWithoutFormat =>
|
||||
Capability::CapabilityStorageImageWriteWithoutFormat =>
|
||||
Some("shader_storage_image_write_without_format"),
|
||||
enums::Capability::CapabilityMultiViewport => Some("multi_viewport"),
|
||||
Capability::CapabilityMultiViewport => Some("multi_viewport"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,17 +9,22 @@
|
||||
|
||||
use std::mem;
|
||||
|
||||
use enums;
|
||||
use parse;
|
||||
use syn::Ident;
|
||||
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.
|
||||
pub fn has_specialization_constants(doc: &parse::Spirv) -> bool {
|
||||
pub fn has_specialization_constants(doc: &Spirv) -> bool {
|
||||
for instruction in doc.instructions.iter() {
|
||||
match instruction {
|
||||
&parse::Instruction::SpecConstantTrue { .. } => return true,
|
||||
&parse::Instruction::SpecConstantFalse { .. } => return true,
|
||||
&parse::Instruction::SpecConstant { .. } => return true,
|
||||
&parse::Instruction::SpecConstantComposite { .. } => return true,
|
||||
&Instruction::SpecConstantTrue { .. } => return true,
|
||||
&Instruction::SpecConstantFalse { .. } => return true,
|
||||
&Instruction::SpecConstant { .. } => 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
|
||||
/// 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 {
|
||||
name: String,
|
||||
constant_id: u32,
|
||||
rust_ty: String,
|
||||
rust_ty: TokenStream,
|
||||
rust_size: usize,
|
||||
rust_alignment: usize,
|
||||
default_value: String,
|
||||
rust_alignment: u32,
|
||||
default_value: TokenStream,
|
||||
}
|
||||
|
||||
let mut spec_consts = Vec::new();
|
||||
|
||||
for instruction in doc.instructions.iter() {
|
||||
let (type_id, result_id, default_value) = match instruction {
|
||||
&parse::Instruction::SpecConstantTrue {
|
||||
result_type_id,
|
||||
result_id,
|
||||
} => {
|
||||
(result_type_id, result_id, "1u32".to_string())
|
||||
},
|
||||
&parse::Instruction::SpecConstantFalse {
|
||||
result_type_id,
|
||||
result_id,
|
||||
} => {
|
||||
(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);
|
||||
&Instruction::SpecConstantTrue { result_type_id, result_id } =>
|
||||
(result_type_id, result_id, quote!{1u32}),
|
||||
|
||||
&Instruction::SpecConstantFalse { result_type_id, result_id } =>
|
||||
(result_type_id, result_id, quote!{0u32}),
|
||||
|
||||
&Instruction::SpecConstant { result_type_id, result_id, ref data } => {
|
||||
let def_val = quote!{
|
||||
unsafe {{ ::std::mem::transmute([ #( #data ),* ]) }}
|
||||
};
|
||||
(result_type_id, result_id, def_val)
|
||||
},
|
||||
&parse::Instruction::SpecConstantComposite {
|
||||
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);
|
||||
}
|
||||
&Instruction::SpecConstantComposite { result_type_id, result_id, ref data } => {
|
||||
let def_val = quote!{
|
||||
unsafe {{ ::std::mem::transmute([ #( #data ),* ]) }}
|
||||
};
|
||||
(result_type_id, result_id, def_val)
|
||||
},
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
@ -88,105 +75,93 @@ pub fn write_specialization_constants(doc: &parse::Spirv) -> String {
|
||||
let constant_id = doc.instructions
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
&parse::Instruction::Decorate {
|
||||
target_id,
|
||||
decoration: enums::Decoration::DecorationSpecId,
|
||||
ref params,
|
||||
} if target_id == result_id => {
|
||||
Some(params[0])
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
&Instruction::Decorate { target_id, decoration: Decoration::DecorationSpecId, ref params }
|
||||
if target_id == result_id => Some(params[0]),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.expect("Found a specialization constant with no SpecId decoration");
|
||||
|
||||
spec_consts.push(SpecConst {
|
||||
name: ::name_from_id(doc, result_id),
|
||||
constant_id,
|
||||
rust_ty,
|
||||
rust_size,
|
||||
rust_alignment,
|
||||
default_value,
|
||||
});
|
||||
name: spirv_search::name_from_id(doc, result_id),
|
||||
constant_id,
|
||||
rust_ty,
|
||||
rust_size,
|
||||
rust_alignment: rust_alignment as u32,
|
||||
default_value,
|
||||
});
|
||||
}
|
||||
|
||||
let map_entries = {
|
||||
let mut map_entries = Vec::new();
|
||||
let mut curr_offset = 0;
|
||||
for c in &spec_consts {
|
||||
map_entries.push(format!(
|
||||
"SpecializationMapEntry {{
|
||||
constant_id: \
|
||||
{},
|
||||
offset: {},
|
||||
size: {},
|
||||
\
|
||||
}}",
|
||||
c.constant_id,
|
||||
curr_offset,
|
||||
c.rust_size
|
||||
));
|
||||
for spec_const in &spec_consts {
|
||||
let constant_id = spec_const.constant_id;
|
||||
let rust_size = spec_const.rust_size;
|
||||
map_entries.push(quote!{
|
||||
SpecializationMapEntry {
|
||||
constant_id: #constant_id,
|
||||
offset: #curr_offset,
|
||||
size: #rust_size,
|
||||
}
|
||||
});
|
||||
|
||||
assert_ne!(c.rust_size, 0);
|
||||
curr_offset += c.rust_size;
|
||||
curr_offset = c.rust_alignment * (1 + (curr_offset - 1) / c.rust_alignment);
|
||||
assert_ne!(spec_const.rust_size, 0);
|
||||
curr_offset += spec_const.rust_size as u32;
|
||||
curr_offset = spec_const.rust_alignment * (1 + (curr_offset - 1) / spec_const.rust_alignment);
|
||||
}
|
||||
map_entries
|
||||
};
|
||||
|
||||
format!(
|
||||
r#"
|
||||
let num_map_entries = map_entries.len();
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct SpecializationConstants {{
|
||||
{struct_def}
|
||||
}}
|
||||
let mut struct_members = vec!();
|
||||
let mut struct_member_defaults = vec!();
|
||||
for spec_const in spec_consts {
|
||||
let name = Ident::new(&spec_const.name, Span::call_site());
|
||||
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 {{
|
||||
fn default() -> SpecializationConstants {{
|
||||
SpecializationConstants {{
|
||||
{def_vals}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
quote!{
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct SpecializationConstants {
|
||||
#( #struct_members ),*
|
||||
}
|
||||
|
||||
unsafe impl SpecConstsTrait for SpecializationConstants {{
|
||||
fn descriptors() -> &'static [SpecializationMapEntry] {{
|
||||
static DESCRIPTORS: [SpecializationMapEntry; {num_map_entries}] = [
|
||||
{map_entries}
|
||||
];
|
||||
&DESCRIPTORS
|
||||
}}
|
||||
}}
|
||||
impl Default for SpecializationConstants {
|
||||
fn default() -> SpecializationConstants {
|
||||
SpecializationConstants {
|
||||
#( #struct_member_defaults ),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
struct_def = spec_consts
|
||||
.iter()
|
||||
.map(|c| format!("pub {}: {}", c.name, c.rust_ty))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
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(", ")
|
||||
)
|
||||
unsafe impl SpecConstsTrait for SpecializationConstants {
|
||||
fn descriptors() -> &'static [SpecializationMapEntry] {
|
||||
static DESCRIPTORS: [SpecializationMapEntry; #num_map_entries] = [
|
||||
#( #map_entries ),*
|
||||
];
|
||||
&DESCRIPTORS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
match instruction {
|
||||
&parse::Instruction::TypeBool { result_id } if result_id == searched => {
|
||||
return ("u32".to_owned(), Some(mem::size_of::<u32>()), mem::align_of::<u32>());
|
||||
&Instruction::TypeBool { result_id } if result_id == searched => {
|
||||
return (quote!{u32}, Some(mem::size_of::<u32>()), mem::align_of::<u32>());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
::structs::type_from_id(doc, searched)
|
||||
structs::type_from_id(doc, searched)
|
||||
}
|
||||
|
190
vulkano-shaders/src/spirv_search.rs
Normal file
190
vulkano-shaders/src/spirv_search.rs
Normal 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
|
||||
}
|
@ -9,55 +9,38 @@
|
||||
|
||||
use std::mem;
|
||||
|
||||
use enums;
|
||||
use parse;
|
||||
use syn::Ident;
|
||||
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.
|
||||
pub fn write_structs(doc: &parse::Spirv) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
pub fn write_structs(doc: &Spirv) -> TokenStream {
|
||||
let mut structs = vec!();
|
||||
for instruction in &doc.instructions {
|
||||
match *instruction {
|
||||
parse::Instruction::TypeStruct {
|
||||
result_id,
|
||||
ref member_types,
|
||||
} => {
|
||||
let (s, _) = write_struct(doc, result_id, member_types);
|
||||
result.push_str(&s);
|
||||
result.push_str("\n");
|
||||
},
|
||||
_ => (),
|
||||
Instruction::TypeStruct { result_id, ref member_types } =>
|
||||
structs.push(write_struct(doc, result_id, member_types).0),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// 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)
|
||||
quote!{
|
||||
#( #structs )*
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>) {
|
||||
let name = ::name_from_id(doc, struct_id);
|
||||
fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, Option<usize>) {
|
||||
let name = Ident::new(&spirv_search::name_from_id(doc, struct_id), Span::call_site());
|
||||
|
||||
// The members of this struct.
|
||||
struct Member {
|
||||
pub name: Ident,
|
||||
pub ty: TokenStream,
|
||||
}
|
||||
let mut rust_members = Vec::with_capacity(members.len());
|
||||
|
||||
// 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() {
|
||||
// Compute infos about the 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
|
||||
// `gl_Position` for example.
|
||||
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.
|
||||
@ -83,10 +66,10 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
match *i {
|
||||
parse::Instruction::MemberDecorate {
|
||||
Instruction::MemberDecorate {
|
||||
target_id,
|
||||
member,
|
||||
decoration: enums::Decoration::DecorationOffset,
|
||||
decoration: Decoration::DecorationOffset,
|
||||
ref params,
|
||||
} if target_id == struct_id && member as usize == num => {
|
||||
return Some(params[0]);
|
||||
@ -102,7 +85,7 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
||||
// variables only. Ignoring these.
|
||||
let spirv_offset = match spirv_offset {
|
||||
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.
|
||||
@ -124,10 +107,9 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
||||
let padding_num = next_padding_num;
|
||||
next_padding_num += 1;
|
||||
rust_members.push(Member {
|
||||
name: format!("_dummy{}", padding_num),
|
||||
value: format!("[u8; {}]", diff),
|
||||
offset: None,
|
||||
});
|
||||
name: Ident::new(&format!("_dummy{}", padding_num), Span::call_site()),
|
||||
ty: quote!{ [u8; #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 {
|
||||
name: member_name.to_owned(),
|
||||
value: ty,
|
||||
offset: Some(spirv_offset),
|
||||
});
|
||||
name: Ident::new(&member_name, Span::call_site()),
|
||||
ty,
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
.iter()
|
||||
.filter_map(|i| match *i {
|
||||
parse::Instruction::Decorate {
|
||||
Instruction::Decorate {
|
||||
target_id,
|
||||
decoration: enums::Decoration::DecorationArrayStride,
|
||||
decoration: Decoration::DecorationArrayStride,
|
||||
ref params,
|
||||
} => {
|
||||
for inst in doc.instructions.iter() {
|
||||
match *inst {
|
||||
parse::Instruction::TypeArray {
|
||||
Instruction::TypeArray {
|
||||
result_id, type_id, ..
|
||||
} if result_id == target_id && type_id == struct_id => {
|
||||
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 => {
|
||||
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();
|
||||
if diff >= 1 {
|
||||
rust_members.push(Member {
|
||||
name: format!("_dummy{}", next_padding_num),
|
||||
value: format!("[u8; {}]", diff),
|
||||
offset: None,
|
||||
});
|
||||
name: Ident::new(&format!("_dummy{}", next_padding_num), Span::call_site()),
|
||||
ty: quote!{ [u8; {}] },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 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 i = format!("\nimpl Clone for {name} {{\n fn clone(&self) -> Self {{\n \
|
||||
{name} {{\n{copies}\n }}\n }}\n}}\n",
|
||||
name = name,
|
||||
copies = rust_members
|
||||
.iter()
|
||||
.map(Member::copy_text)
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n"));
|
||||
(i, "#[derive(Copy)]")
|
||||
let (clone_impl, copy_derive) = if current_rust_offset.is_some() {
|
||||
let mut copies = vec!();
|
||||
for member in &rust_members {
|
||||
let name = &member.name;
|
||||
copies.push(quote!{ #name: self.#name, });
|
||||
}
|
||||
(
|
||||
// Clone is implemented manually because members can be large arrays
|
||||
// that do not implement Clone, but do implement Copy
|
||||
quote!{
|
||||
impl Clone for #name {
|
||||
fn clone(&self) -> Self {
|
||||
#name {
|
||||
#( #copies )*
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
quote!{ #[derive(Copy)] }
|
||||
)
|
||||
} else {
|
||||
("".to_owned(), "")
|
||||
(quote!{}, quote!{})
|
||||
};
|
||||
|
||||
let s =
|
||||
format!("#[repr(C)]\n{derive_text}\n#[allow(non_snake_case)]\npub struct {name} \
|
||||
{{\n{members}\n}} /* total_size: {t:?} */\n{impl_text}",
|
||||
name = name,
|
||||
members = rust_members
|
||||
.iter()
|
||||
.map(Member::declaration_text)
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n"),
|
||||
t = spirv_req_total_size,
|
||||
impl_text = impl_text,
|
||||
derive_text = derive_text);
|
||||
(s,
|
||||
spirv_req_total_size
|
||||
.map(|sz| sz as usize)
|
||||
.or(current_rust_offset))
|
||||
let mut members = vec!();
|
||||
for member in &rust_members {
|
||||
let name = &member.name;
|
||||
let ty = &member.ty;
|
||||
members.push(quote!(pub #name: #ty,));
|
||||
}
|
||||
|
||||
let ast = quote! {
|
||||
#[repr(C)]
|
||||
#copy_derive
|
||||
#[allow(non_snake_case)]
|
||||
pub struct #name {
|
||||
#( #members )*
|
||||
}
|
||||
#clone_impl
|
||||
};
|
||||
|
||||
(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.
|
||||
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 {
|
||||
match *instruction {
|
||||
parse::Instruction::MemberDecorate {
|
||||
target_id,
|
||||
member,
|
||||
decoration: enums::Decoration::DecorationBuiltIn,
|
||||
..
|
||||
} if target_id == id && member == member_id => {
|
||||
return true;
|
||||
},
|
||||
Instruction::MemberDecorate { target_id, member, decoration: 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.
|
||||
///
|
||||
/// 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() {
|
||||
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")
|
||||
},
|
||||
&parse::Instruction::TypeInt {
|
||||
result_id,
|
||||
width,
|
||||
signedness,
|
||||
} if result_id == searched => {
|
||||
}
|
||||
&Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
|
||||
match (width, signedness) {
|
||||
(8, true) => {
|
||||
#[repr(C)]
|
||||
@ -267,7 +249,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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) => {
|
||||
#[repr(C)]
|
||||
@ -276,7 +258,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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) => {
|
||||
#[repr(C)]
|
||||
@ -285,7 +267,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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) => {
|
||||
#[repr(C)]
|
||||
@ -294,7 +276,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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) => {
|
||||
#[repr(C)]
|
||||
@ -303,7 +285,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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) => {
|
||||
#[repr(C)]
|
||||
@ -312,7 +294,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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) => {
|
||||
#[repr(C)]
|
||||
@ -321,7 +303,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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) => {
|
||||
#[repr(C)]
|
||||
@ -330,12 +312,12 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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),
|
||||
}
|
||||
},
|
||||
&parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
|
||||
}
|
||||
&Instruction::TypeFloat { result_id, width } if result_id == searched => {
|
||||
match width {
|
||||
32 => {
|
||||
#[repr(C)]
|
||||
@ -344,7 +326,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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 => {
|
||||
#[repr(C)]
|
||||
@ -353,61 +335,58 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
||||
after: u8,
|
||||
}
|
||||
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),
|
||||
}
|
||||
},
|
||||
&parse::Instruction::TypeVector {
|
||||
}
|
||||
&Instruction::TypeVector {
|
||||
result_id,
|
||||
component_id,
|
||||
count,
|
||||
} if result_id == searched => {
|
||||
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
||||
let (t, t_size, t_align) = type_from_id(doc, component_id);
|
||||
return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
|
||||
},
|
||||
&parse::Instruction::TypeMatrix {
|
||||
let (ty, t_size, t_align) = type_from_id(doc, component_id);
|
||||
let array_length = count as usize;
|
||||
let size = t_size.map(|s| s * count as usize);
|
||||
return (quote!{ [#ty; #array_length] }, size, t_align);
|
||||
}
|
||||
&Instruction::TypeMatrix {
|
||||
result_id,
|
||||
column_type_id,
|
||||
column_count,
|
||||
} if result_id == searched => {
|
||||
// FIXME: row-major or column-major
|
||||
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);
|
||||
return (format!("[{}; {}]", t, column_count),
|
||||
t_size.map(|s| s * column_count as usize),
|
||||
t_align);
|
||||
},
|
||||
&parse::Instruction::TypeArray {
|
||||
let (ty, t_size, t_align) = type_from_id(doc, column_type_id);
|
||||
let array_length = column_count as usize;
|
||||
let size = t_size.map(|s| s * column_count as usize);
|
||||
return (quote!{ [#ty; #array_length] }, size, t_align);
|
||||
}
|
||||
&Instruction::TypeArray {
|
||||
result_id,
|
||||
type_id,
|
||||
length_id,
|
||||
} if result_id == searched => {
|
||||
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 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,
|
||||
})
|
||||
&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);
|
||||
let stride = doc.instructions.iter().filter_map(|e| match e {
|
||||
parse::Instruction::Decorate{
|
||||
Instruction::Decorate {
|
||||
target_id,
|
||||
decoration: enums::Decoration::DecorationArrayStride,
|
||||
decoration: Decoration::DecorationArrayStride,
|
||||
ref params,
|
||||
} if *target_id == searched => {
|
||||
Some(params[0])
|
||||
},
|
||||
} if *target_id == searched => Some(params[0]),
|
||||
_ => None,
|
||||
})
|
||||
.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 \
|
||||
(e.g. increase a vec3 to a vec4)")
|
||||
}
|
||||
return (format!("[{}; {}]", t, len), Some(t_size * len as usize), t_align);
|
||||
},
|
||||
&parse::Instruction::TypeRuntimeArray { result_id, type_id }
|
||||
let array_length = len as usize;
|
||||
let size = Some(t_size * len as usize);
|
||||
return (quote!{ [#ty; #array_length] }, size, t_align);
|
||||
}
|
||||
&Instruction::TypeRuntimeArray { result_id, type_id }
|
||||
if result_id == searched => {
|
||||
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
||||
let (t, _, t_align) = type_from_id(doc, type_id);
|
||||
return (format!("[{}]", t), None, t_align);
|
||||
},
|
||||
&parse::Instruction::TypeStruct {
|
||||
let (ty, _, t_align) = type_from_id(doc, type_id);
|
||||
return (quote!{ [#ty] }, None, t_align);
|
||||
}
|
||||
&Instruction::TypeStruct {
|
||||
result_id,
|
||||
ref member_types,
|
||||
} if result_id == searched => {
|
||||
// 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 align = member_types
|
||||
.iter()
|
||||
.map(|&t| type_from_id(doc, t).2)
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
return (name, size, align);
|
||||
return (ty, size, align);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user