mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 06:45:23 +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)
|
# Unreleased (Breaking)
|
||||||
|
|
||||||
|
- `vulkano_shaders::reflect` now returns `Result<proc_macro2::TokenStream, Error>` instead of `Result<String, Error>`
|
||||||
- Removed mir support, as it is being removed from the vulkan spec.
|
- Removed mir support, as it is being removed from the vulkan spec.
|
||||||
- Remove vulkano_shaders::build_glsl_shaders
|
- Remove vulkano_shaders::build_glsl_shaders
|
||||||
- Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage`
|
- Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage`
|
||||||
|
@ -153,7 +153,7 @@ void main() {
|
|||||||
// We need to signal a fence here because below we want to block the CPU until the GPU has
|
// We need to signal a fence here because below we want to block the CPU until the GPU has
|
||||||
// reached that point in the execution.
|
// reached that point in the execution.
|
||||||
.then_signal_fence_and_flush().unwrap();
|
.then_signal_fence_and_flush().unwrap();
|
||||||
|
|
||||||
// Blocks execution until the GPU has finished the operation. This method only exists on the
|
// Blocks execution until the GPU has finished the operation. This method only exists on the
|
||||||
// future that corresponds to a signalled fence. In other words, this method wouldn't be
|
// future that corresponds to a signalled fence. In other words, this method wouldn't be
|
||||||
// available if we didn't call `.then_signal_fence_and_flush()` earlier.
|
// available if we didn't call `.then_signal_fence_and_flush()` earlier.
|
||||||
|
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]
|
[dependencies]
|
||||||
syn = "0.15"
|
syn = "0.15"
|
||||||
|
quote = "0.6"
|
||||||
|
proc-macro2 = "0.4"
|
||||||
vulkano-shaders = { version = "0.10", path = "../vulkano-shaders" }
|
vulkano-shaders = { version = "0.10", path = "../vulkano-shaders" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -156,6 +156,8 @@
|
|||||||
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
|
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
extern crate proc_macro2;
|
||||||
|
extern crate quote;
|
||||||
extern crate syn;
|
extern crate syn;
|
||||||
extern crate vulkano_shaders;
|
extern crate vulkano_shaders;
|
||||||
|
|
||||||
@ -164,15 +166,13 @@ use std::fs::File;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
|
|
||||||
enum SourceKind {
|
enum SourceKind {
|
||||||
Src(String),
|
Src(String),
|
||||||
Path(String),
|
Path(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(VulkanoShader, attributes(src, path, ty))]
|
#[proc_macro_derive(VulkanoShader, attributes(src, path, ty))]
|
||||||
pub fn derive(input: TokenStream) -> TokenStream {
|
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let syn_item: syn::DeriveInput = syn::parse(input).unwrap();
|
let syn_item: syn::DeriveInput = syn::parse(input).unwrap();
|
||||||
|
|
||||||
let source_code = {
|
let source_code = {
|
||||||
@ -244,8 +244,5 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
|||||||
};
|
};
|
||||||
let content = vulkano_shaders::compile(&source_code, ty).unwrap();
|
let content = vulkano_shaders::compile(&source_code, ty).unwrap();
|
||||||
|
|
||||||
vulkano_shaders::reflect("Shader", content.as_binary())
|
vulkano_shaders::reflect("Shader", content.as_binary()).unwrap().into()
|
||||||
.unwrap()
|
|
||||||
.parse()
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
@ -10,3 +10,6 @@ categories = ["rendering::graphics-api"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
shaderc = "0.3"
|
shaderc = "0.3"
|
||||||
|
syn = "0.15"
|
||||||
|
quote = "0.6"
|
||||||
|
proc-macro2 = "0.4"
|
||||||
|
@ -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 std::cmp;
|
||||||
|
|
||||||
use enums;
|
use proc_macro2::TokenStream;
|
||||||
use parse;
|
|
||||||
|
|
||||||
pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
|
use enums::{Dim, Decoration, StorageClass, ImageFormat};
|
||||||
|
use parse::{Instruction, Spirv};
|
||||||
|
use spirv_search;
|
||||||
|
|
||||||
|
pub fn write_descriptor_sets(doc: &Spirv) -> TokenStream {
|
||||||
// TODO: not implemented correctly
|
// TODO: not implemented correctly
|
||||||
|
|
||||||
// Finding all the descriptors.
|
// Finding all the descriptors.
|
||||||
@ -20,42 +23,35 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
|
|||||||
struct Descriptor {
|
struct Descriptor {
|
||||||
set: u32,
|
set: u32,
|
||||||
binding: u32,
|
binding: u32,
|
||||||
desc_ty: String,
|
desc_ty: TokenStream,
|
||||||
array_count: u64,
|
array_count: u64,
|
||||||
readonly: bool,
|
readonly: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looping to find all the elements that have the `DescriptorSet` decoration.
|
// Looping to find all the elements that have the `DescriptorSet` decoration.
|
||||||
for instruction in doc.instructions.iter() {
|
for instruction in doc.instructions.iter() {
|
||||||
let (variable_id, descriptor_set) = match instruction {
|
let (variable_id, set) = match instruction {
|
||||||
&parse::Instruction::Decorate {
|
&Instruction::Decorate { target_id, decoration: Decoration::DecorationDescriptorSet, ref params }
|
||||||
target_id,
|
=> (target_id, params[0]),
|
||||||
decoration: enums::Decoration::DecorationDescriptorSet,
|
|
||||||
ref params,
|
|
||||||
} => {
|
|
||||||
(target_id, params[0])
|
|
||||||
},
|
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find which type is pointed to by this variable.
|
// Find which type is pointed to by this variable.
|
||||||
let pointed_ty = pointer_variable_ty(doc, variable_id);
|
let pointed_ty = pointer_variable_ty(doc, variable_id);
|
||||||
// Name of the variable.
|
// Name of the variable.
|
||||||
let name = ::name_from_id(doc, variable_id);
|
let name = spirv_search::name_from_id(doc, variable_id);
|
||||||
|
|
||||||
// Find the binding point of this descriptor.
|
// Find the binding point of this descriptor.
|
||||||
let binding = doc.instructions
|
let binding = doc.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|i| {
|
.filter_map(|i| {
|
||||||
match i {
|
match i {
|
||||||
&parse::Instruction::Decorate {
|
&Instruction::Decorate {
|
||||||
target_id,
|
target_id,
|
||||||
decoration: enums::Decoration::DecorationBinding,
|
decoration: Decoration::DecorationBinding,
|
||||||
ref params,
|
ref params,
|
||||||
} if target_id == variable_id => {
|
} if target_id == variable_id => Some(params[0]),
|
||||||
Some(params[0])
|
_ => None, // TODO: other types
|
||||||
},
|
|
||||||
_ => None, // TODO: other types
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.next()
|
.next()
|
||||||
@ -63,31 +59,20 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
|
|||||||
|
|
||||||
// Find information about the kind of binding for this descriptor.
|
// Find information about the kind of binding for this descriptor.
|
||||||
let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
|
let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
|
||||||
.expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe \
|
.expect(&format!(
|
||||||
unimplemented)",
|
"Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)",
|
||||||
name,
|
name,
|
||||||
pointed_ty));
|
pointed_ty
|
||||||
|
));
|
||||||
descriptors.push(Descriptor {
|
descriptors.push(Descriptor { desc_ty, set, binding, array_count, readonly });
|
||||||
desc_ty: desc_ty,
|
|
||||||
set: descriptor_set,
|
|
||||||
binding: binding,
|
|
||||||
array_count: array_count,
|
|
||||||
readonly: readonly,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looping to find all the push constant structs.
|
// Looping to find all the push constant structs.
|
||||||
let mut push_constants_size = 0;
|
let mut push_constants_size = 0;
|
||||||
for instruction in doc.instructions.iter() {
|
for instruction in doc.instructions.iter() {
|
||||||
let type_id = match instruction {
|
let type_id = match instruction {
|
||||||
&parse::Instruction::TypePointer {
|
&Instruction::TypePointer { type_id, storage_class: StorageClass::StorageClassPushConstant, .. }
|
||||||
type_id,
|
=> type_id,
|
||||||
storage_class: enums::StorageClass::StorageClassPushConstant,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
type_id
|
|
||||||
},
|
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,127 +85,106 @@ pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
|
|||||||
let descriptor_body = descriptors
|
let descriptor_body = descriptors
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| {
|
.map(|d| {
|
||||||
format!(
|
let set = d.set as usize;
|
||||||
"({set}, {binding}) => Some(DescriptorDesc {{
|
let binding = d.binding as usize;
|
||||||
ty: {desc_ty},
|
let desc_ty = &d.desc_ty;
|
||||||
array_count: {array_count},
|
let array_count = d.array_count as u32;
|
||||||
stages: self.0.clone(),
|
let readonly = d.readonly;
|
||||||
readonly: {readonly},
|
quote!{
|
||||||
}}),",
|
(#set, #binding) => Some(DescriptorDesc {
|
||||||
set = d.set,
|
ty: #desc_ty,
|
||||||
binding = d.binding,
|
array_count: #array_count,
|
||||||
desc_ty = d.desc_ty,
|
stages: self.0.clone(),
|
||||||
array_count = d.array_count,
|
readonly: #readonly,
|
||||||
readonly = if d.readonly { "true" } else { "false" }
|
}),
|
||||||
)
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>();
|
||||||
.concat();
|
|
||||||
|
|
||||||
let num_sets = descriptors.iter().fold(0, |s, d| cmp::max(s, d.set + 1));
|
let num_sets = descriptors.iter().fold(0, |s, d| cmp::max(s, d.set + 1)) as usize;
|
||||||
|
|
||||||
// Writing the body of the `num_bindings_in_set` method.
|
// Writing the body of the `num_bindings_in_set` method.
|
||||||
let num_bindings_in_set_body = {
|
let num_bindings_in_set_body = (0 .. num_sets)
|
||||||
(0 .. num_sets)
|
.map(|set| {
|
||||||
.map(|set| {
|
let num = descriptors
|
||||||
let num = descriptors
|
.iter()
|
||||||
.iter()
|
.filter(|d| d.set == set as u32)
|
||||||
.filter(|d| d.set == set)
|
.fold(0, |s, d| cmp::max(s, 1 + d.binding)) as usize;
|
||||||
.fold(0, |s, d| cmp::max(s, 1 + d.binding));
|
quote!{ #set => Some(#num), }
|
||||||
format!("{set} => Some({num}),", set = set, num = num)
|
})
|
||||||
})
|
.collect::<Vec<_>>();
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.concat()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Writing the body of the `num_push_constants_ranges` method.
|
// Writing the body of the `num_push_constants_ranges` method.
|
||||||
let num_push_constants_ranges_body = {
|
let num_push_constants_ranges_body = if push_constants_size == 0 { 0 } else { 1 } as usize;
|
||||||
if push_constants_size == 0 { "0" } else { "1" }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Writing the body of the `push_constants_range` method.
|
// Writing the body of the `push_constants_range` method.
|
||||||
let push_constants_range_body = format!(
|
let push_constants_range_body = quote!(
|
||||||
r#"
|
if num != 0 || #push_constants_size == 0 {
|
||||||
if num != 0 || {pc_size} == 0 {{ return None; }}
|
None
|
||||||
Some(PipelineLayoutDescPcRange {{
|
} else {
|
||||||
offset: 0, // FIXME: not necessarily true
|
Some(PipelineLayoutDescPcRange {
|
||||||
size: {pc_size},
|
offset: 0, // FIXME: not necessarily true
|
||||||
stages: ShaderStages::all(), // FIXME: wrong
|
size: #push_constants_size,
|
||||||
}})
|
stages: ShaderStages::all(), // FIXME: wrong
|
||||||
"#,
|
})
|
||||||
pc_size = push_constants_size
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
format!(
|
quote!{
|
||||||
r#"
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Layout(pub ShaderStages);
|
pub struct Layout(pub ShaderStages);
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe impl PipelineLayoutDesc for Layout {{
|
unsafe impl PipelineLayoutDesc for Layout {
|
||||||
fn num_sets(&self) -> usize {{
|
fn num_sets(&self) -> usize {
|
||||||
{num_sets}
|
#num_sets
|
||||||
}}
|
}
|
||||||
|
|
||||||
fn num_bindings_in_set(&self, set: usize) -> Option<usize> {{
|
fn num_bindings_in_set(&self, set: usize) -> Option<usize> {
|
||||||
match set {{
|
match set {
|
||||||
{num_bindings_in_set_body}
|
#( #num_bindings_in_set_body )*
|
||||||
_ => None
|
_ => None
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
|
|
||||||
fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {{
|
fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {
|
||||||
match (set, binding) {{
|
match (set, binding) {
|
||||||
{descriptor_body}
|
#( #descriptor_body )*
|
||||||
_ => None
|
_ => None
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
|
|
||||||
fn num_push_constants_ranges(&self) -> usize {{
|
fn num_push_constants_ranges(&self) -> usize {
|
||||||
{num_push_constants_ranges_body}
|
#num_push_constants_ranges_body
|
||||||
}}
|
}
|
||||||
|
|
||||||
fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {{
|
fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {
|
||||||
{push_constants_range_body}
|
#push_constants_range_body
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
"#,
|
}
|
||||||
num_sets = num_sets,
|
|
||||||
num_bindings_in_set_body = num_bindings_in_set_body,
|
|
||||||
descriptor_body = descriptor_body,
|
|
||||||
num_push_constants_ranges_body = num_push_constants_ranges_body,
|
|
||||||
push_constants_range_body = push_constants_range_body
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
|
/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
|
||||||
/// type.
|
/// type.
|
||||||
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
|
fn pointer_variable_ty(doc: &Spirv, variable: u32) -> u32 {
|
||||||
let var_ty = doc.instructions
|
let var_ty = doc.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|i| match i {
|
.filter_map(|i| match i {
|
||||||
&parse::Instruction::Variable {
|
&Instruction::Variable { result_type_id, result_id, .. }
|
||||||
result_type_id,
|
if result_id == variable => Some(result_type_id),
|
||||||
result_id,
|
_ => None,
|
||||||
..
|
})
|
||||||
} if result_id == variable => {
|
|
||||||
Some(result_type_id)
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.next()
|
.next()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
doc.instructions
|
doc.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|i| match i {
|
.filter_map(|i| match i {
|
||||||
&parse::Instruction::TypePointer { result_id, type_id, .. }
|
&Instruction::TypePointer { result_id, type_id, .. }
|
||||||
if result_id == var_ty => {
|
if result_id == var_ty => Some(type_id),
|
||||||
Some(type_id)
|
_ => None,
|
||||||
},
|
})
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
@ -229,26 +193,21 @@ fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
|
|||||||
/// read-only, and the number of array elements.
|
/// read-only, and the number of array elements.
|
||||||
///
|
///
|
||||||
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
|
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
|
||||||
fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
|
fn descriptor_infos(doc: &Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
|
||||||
-> Option<(String, bool, u64)> {
|
-> Option<(TokenStream, bool, u64)>
|
||||||
|
{
|
||||||
doc.instructions.iter().filter_map(|i| {
|
doc.instructions.iter().filter_map(|i| {
|
||||||
match i {
|
match i {
|
||||||
&parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
|
&Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
|
||||||
// Determine whether there's a Block or BufferBlock decoration.
|
// Determine whether there's a Block or BufferBlock decoration.
|
||||||
let is_ssbo = doc.instructions.iter().filter_map(|i| {
|
let is_ssbo = doc.instructions.iter().filter_map(|i| {
|
||||||
match i {
|
match i {
|
||||||
&parse::Instruction::Decorate
|
&Instruction::Decorate
|
||||||
{ target_id, decoration: enums::Decoration::DecorationBufferBlock, .. }
|
{ target_id, decoration: Decoration::DecorationBufferBlock, .. }
|
||||||
if target_id == pointed_ty =>
|
if target_id == pointed_ty => Some(true),
|
||||||
{
|
&Instruction::Decorate
|
||||||
Some(true)
|
{ target_id, decoration: Decoration::DecorationBlock, .. }
|
||||||
},
|
if target_id == pointed_ty => Some(false),
|
||||||
&parse::Instruction::Decorate
|
|
||||||
{ target_id, decoration: enums::Decoration::DecorationBlock, .. }
|
|
||||||
if target_id == pointed_ty =>
|
|
||||||
{
|
|
||||||
Some(false)
|
|
||||||
},
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
|
}).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
|
||||||
@ -257,103 +216,111 @@ fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sa
|
|||||||
// Determine whether there's a NonWritable decoration.
|
// Determine whether there's a NonWritable decoration.
|
||||||
//let non_writable = false; // TODO: tricky because the decoration is on struct members
|
//let non_writable = false; // TODO: tricky because the decoration is on struct members
|
||||||
|
|
||||||
let desc = format!("DescriptorDescTy::Buffer(DescriptorBufferDesc {{
|
let desc = quote!{
|
||||||
dynamic: Some(false),
|
DescriptorDescTy::Buffer(DescriptorBufferDesc {
|
||||||
storage: {},
|
dynamic: Some(false),
|
||||||
}})", if is_ssbo { "true" } else { "false "});
|
storage: #is_ssbo,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
Some((desc, true, 1))
|
Some((desc, true, 1))
|
||||||
},
|
}
|
||||||
|
&Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled, ref format, .. }
|
||||||
&parse::Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled,
|
if result_id == pointed_ty =>
|
||||||
ref format, .. } if result_id == pointed_ty =>
|
|
||||||
{
|
{
|
||||||
let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
|
let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
|
||||||
have a Sampled operand of 1 or 2");
|
have a Sampled operand of 1 or 2");
|
||||||
|
|
||||||
let ms = if ms { "true" } else { "false" };
|
let arrayed = match arrayed {
|
||||||
let arrayed = if arrayed {
|
true => quote!{ DescriptorImageDescArray::Arrayed { max_layers: None } },
|
||||||
"DescriptorImageDescArray::Arrayed { max_layers: None }"
|
false => quote!{ DescriptorImageDescArray::NonArrayed }
|
||||||
} else {
|
|
||||||
"DescriptorImageDescArray::NonArrayed"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let &enums::Dim::DimSubpassData = dim {
|
match dim {
|
||||||
// We are an input attachment.
|
Dim::DimSubpassData => {
|
||||||
assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
|
// We are an input attachment.
|
||||||
an OpTypeImage whose dimension is \
|
assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
|
||||||
SubpassData");
|
an OpTypeImage whose dimension is \
|
||||||
assert!(if let &enums::ImageFormat::ImageFormatUnknown = format { true }
|
SubpassData");
|
||||||
else { false }, "If Dim is SubpassData, Image Format must be Unknown");
|
assert!(if let &ImageFormat::ImageFormatUnknown = format { true }
|
||||||
assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");
|
else { false }, "If Dim is SubpassData, Image Format must be Unknown");
|
||||||
|
assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");
|
||||||
|
|
||||||
let desc = format!("DescriptorDescTy::InputAttachment {{
|
let desc = quote!{
|
||||||
multisampled: {},
|
DescriptorDescTy::InputAttachment {
|
||||||
array_layers: {}
|
multisampled: #ms,
|
||||||
}}", ms, arrayed);
|
array_layers: #arrayed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Some((desc, true, 1))
|
Some((desc, true, 1))
|
||||||
|
}
|
||||||
|
Dim::DimBuffer => {
|
||||||
|
// We are a texel buffer.
|
||||||
|
let not_sampled = !sampled;
|
||||||
|
let desc = quote!{
|
||||||
|
DescriptorDescTy::TexelBuffer {
|
||||||
|
storage: #not_sampled,
|
||||||
|
format: None, // TODO: specify format if known
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} else if let &enums::Dim::DimBuffer = dim {
|
Some((desc, true, 1))
|
||||||
// We are a texel buffer.
|
}
|
||||||
let desc = format!("DescriptorDescTy::TexelBuffer {{
|
_ => {
|
||||||
storage: {},
|
// We are a sampled or storage image.
|
||||||
format: None, // TODO: specify format if known
|
let ty = match force_combined_image_sampled {
|
||||||
}}", !sampled);
|
true => quote!{ DescriptorDescTy::CombinedImageSampler },
|
||||||
|
false => quote!{ DescriptorDescTy::Image }
|
||||||
|
};
|
||||||
|
let dim = match *dim {
|
||||||
|
Dim::Dim1D => quote!{ DescriptorImageDescDimensions::OneDimensional },
|
||||||
|
Dim::Dim2D => quote!{ DescriptorImageDescDimensions::TwoDimensional },
|
||||||
|
Dim::Dim3D => quote!{ DescriptorImageDescDimensions::ThreeDimensional },
|
||||||
|
Dim::DimCube => quote!{ DescriptorImageDescDimensions::Cube },
|
||||||
|
Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
Some((desc, true, 1))
|
let desc = quote!{
|
||||||
|
#ty(DescriptorImageDesc {
|
||||||
|
sampled: #sampled,
|
||||||
|
dimensions: #dim,
|
||||||
|
format: None, // TODO: specify format if known
|
||||||
|
multisampled: #ms,
|
||||||
|
array_layers: #arrayed,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
} else {
|
Some((desc, true, 1))
|
||||||
// We are a sampled or storage image.
|
}
|
||||||
let sampled = if sampled { "true" } else { "false" };
|
|
||||||
let ty = if force_combined_image_sampled { "CombinedImageSampler" }
|
|
||||||
else { "Image" };
|
|
||||||
let dim = match *dim {
|
|
||||||
enums::Dim::Dim1D => "DescriptorImageDescDimensions::OneDimensional",
|
|
||||||
enums::Dim::Dim2D => "DescriptorImageDescDimensions::TwoDimensional",
|
|
||||||
enums::Dim::Dim3D => "DescriptorImageDescDimensions::ThreeDimensional",
|
|
||||||
enums::Dim::DimCube => "DescriptorImageDescDimensions::Cube",
|
|
||||||
enums::Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
|
|
||||||
_ => unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
let desc = format!("DescriptorDescTy::{}(DescriptorImageDesc {{
|
|
||||||
sampled: {},
|
|
||||||
dimensions: {},
|
|
||||||
format: None, // TODO: specify format if known
|
|
||||||
multisampled: {},
|
|
||||||
array_layers: {},
|
|
||||||
}})", ty, sampled, dim, ms, arrayed);
|
|
||||||
|
|
||||||
Some((desc, true, 1))
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
&parse::Instruction::TypeSampledImage { result_id, image_type_id }
|
&Instruction::TypeSampledImage { result_id, image_type_id } if result_id == pointed_ty
|
||||||
if result_id == pointed_ty =>
|
=> descriptor_infos(doc, image_type_id, true),
|
||||||
{
|
|
||||||
descriptor_infos(doc, image_type_id, true)
|
|
||||||
},
|
|
||||||
|
|
||||||
&parse::Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
|
&Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
|
||||||
let desc = format!("DescriptorDescTy::Sampler");
|
let desc = quote!{ DescriptorDescTy::Sampler };
|
||||||
Some((desc, true, 1))
|
Some((desc, true, 1))
|
||||||
},
|
}
|
||||||
|
&Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
|
||||||
&parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
|
|
||||||
let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
|
let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
|
||||||
None => return None,
|
None => return None,
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
};
|
};
|
||||||
assert_eq!(arr, 1); // TODO: implement?
|
assert_eq!(arr, 1); // TODO: implement?
|
||||||
let len = doc.instructions.iter().filter_map(|e| {
|
let len = doc.instructions.iter().filter_map(|e| {
|
||||||
match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
|
match e {
|
||||||
|
&Instruction::Constant { result_id, ref data, .. }
|
||||||
|
if result_id == length_id => Some(data.clone()),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
}).next().expect("failed to find array length");
|
}).next().expect("failed to find array length");
|
||||||
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
|
let len = len.iter().rev().fold(0, |a, &b| (a << 32) | b as u64);
|
||||||
Some((desc, readonly, len))
|
Some((desc, readonly, len))
|
||||||
},
|
}
|
||||||
|
_ => None, // TODO: other types
|
||||||
_ => None, // TODO: other types
|
|
||||||
}
|
}
|
||||||
}).next()
|
}).next()
|
||||||
}
|
}
|
||||||
|
@ -7,25 +7,17 @@
|
|||||||
// notice may not be copied, modified, or distributed except
|
// notice may not be copied, modified, or distributed except
|
||||||
// according to those terms.
|
// according to those terms.
|
||||||
|
|
||||||
use enums;
|
use syn::Ident;
|
||||||
use parse;
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
|
||||||
use format_from_id;
|
use enums::{StorageClass, ExecutionModel, ExecutionMode};
|
||||||
use is_builtin;
|
use parse::{Instruction, Spirv};
|
||||||
use location_decoration;
|
use spirv_search;
|
||||||
use name_from_id;
|
|
||||||
|
|
||||||
pub fn write_entry_point(doc: &parse::Spirv, instruction: &parse::Instruction) -> (String, String) {
|
pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream, TokenStream) {
|
||||||
let (execution, id, ep_name, interface) = match instruction {
|
let (execution, id, ep_name, interface) = match instruction {
|
||||||
&parse::Instruction::EntryPoint {
|
&Instruction::EntryPoint { ref execution, id, ref name, ref interface, .. } =>
|
||||||
ref execution,
|
(execution, id, name, interface),
|
||||||
id,
|
|
||||||
ref name,
|
|
||||||
ref interface,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
(execution, id, name, interface)
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,230 +28,235 @@ pub fn write_entry_point(doc: &parse::Spirv, instruction: &parse::Instruction) -
|
|||||||
.chain(ep_name.chars().skip(1))
|
.chain(ep_name.chars().skip(1))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let interface_structs =
|
let ignore_first_array_in = match *execution {
|
||||||
write_interface_structs(doc,
|
ExecutionModel::ExecutionModelTessellationControl => true,
|
||||||
&capitalized_ep_name,
|
ExecutionModel::ExecutionModelTessellationEvaluation => true,
|
||||||
interface,
|
ExecutionModel::ExecutionModelGeometry => true,
|
||||||
match *execution {
|
_ => false,
|
||||||
enums::ExecutionModel::ExecutionModelTessellationControl =>
|
};
|
||||||
true,
|
let ignore_first_array_out = match *execution {
|
||||||
enums::ExecutionModel::ExecutionModelTessellationEvaluation =>
|
ExecutionModel::ExecutionModelTessellationControl => true,
|
||||||
true,
|
_ => false,
|
||||||
enums::ExecutionModel::ExecutionModelGeometry => true,
|
};
|
||||||
_ => false,
|
|
||||||
},
|
let interface_structs = write_interface_structs(
|
||||||
match *execution {
|
doc,
|
||||||
enums::ExecutionModel::ExecutionModelTessellationControl =>
|
&capitalized_ep_name,
|
||||||
true,
|
interface,
|
||||||
_ => false,
|
ignore_first_array_in,
|
||||||
});
|
ignore_first_array_out
|
||||||
|
);
|
||||||
|
|
||||||
let spec_consts_struct = if ::spec_consts::has_specialization_constants(doc) {
|
let spec_consts_struct = if ::spec_consts::has_specialization_constants(doc) {
|
||||||
"SpecializationConstants"
|
quote!{ SpecializationConstants }
|
||||||
} else {
|
} else {
|
||||||
"()"
|
quote!{ () }
|
||||||
};
|
};
|
||||||
|
|
||||||
let (ty, f_call) = {
|
let (ty, f_call) = {
|
||||||
if let enums::ExecutionModel::ExecutionModelGLCompute = *execution {
|
if let ExecutionModel::ExecutionModelGLCompute = *execution {
|
||||||
(format!("::vulkano::pipeline::shader::ComputeEntryPoint<{}, Layout>",
|
(
|
||||||
spec_consts_struct),
|
quote!{ ::vulkano::pipeline::shader::ComputeEntryPoint<#spec_consts_struct, Layout> },
|
||||||
format!("compute_entry_point(::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _), \
|
quote!{ compute_entry_point(
|
||||||
Layout(ShaderStages {{ compute: true, .. ShaderStages::none() }}))"))
|
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
|
||||||
|
Layout(ShaderStages { compute: true, .. ShaderStages::none() })
|
||||||
|
)}
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let ty = match *execution {
|
let entry_ty = match *execution {
|
||||||
enums::ExecutionModel::ExecutionModelVertex => {
|
ExecutionModel::ExecutionModelVertex =>
|
||||||
"::vulkano::pipeline::shader::GraphicsShaderType::Vertex".to_owned()
|
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::Vertex },
|
||||||
},
|
|
||||||
|
|
||||||
enums::ExecutionModel::ExecutionModelTessellationControl => {
|
ExecutionModel::ExecutionModelTessellationControl =>
|
||||||
"::vulkano::pipeline::shader::GraphicsShaderType::TessellationControl"
|
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::TessellationControl },
|
||||||
.to_owned()
|
|
||||||
},
|
|
||||||
|
|
||||||
enums::ExecutionModel::ExecutionModelTessellationEvaluation => {
|
ExecutionModel::ExecutionModelTessellationEvaluation =>
|
||||||
"::vulkano::pipeline::shader::GraphicsShaderType::TessellationEvaluation"
|
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::TessellationEvaluation },
|
||||||
.to_owned()
|
|
||||||
},
|
|
||||||
|
|
||||||
enums::ExecutionModel::ExecutionModelGeometry => {
|
ExecutionModel::ExecutionModelGeometry => {
|
||||||
let mut execution_mode = None;
|
let mut execution_mode = None;
|
||||||
|
|
||||||
for instruction in doc.instructions.iter() {
|
for instruction in doc.instructions.iter() {
|
||||||
if let &parse::Instruction::ExecutionMode {
|
if let &Instruction::ExecutionMode { target_id, ref mode, .. } = instruction {
|
||||||
target_id,
|
if target_id == id {
|
||||||
ref mode,
|
execution_mode = match mode {
|
||||||
..
|
&ExecutionMode::ExecutionModeInputPoints => Some(quote!{ Points }),
|
||||||
} = instruction
|
&ExecutionMode::ExecutionModeInputLines => Some(quote!{ Lines }),
|
||||||
{
|
&ExecutionMode::ExecutionModeInputLinesAdjacency =>
|
||||||
if target_id != id {
|
Some(quote!{ LinesWithAdjacency }),
|
||||||
continue;
|
&ExecutionMode::ExecutionModeTriangles => Some(quote!{ Triangles }),
|
||||||
|
&ExecutionMode::ExecutionModeInputTrianglesAdjacency =>
|
||||||
|
Some(quote!{ TrianglesWithAdjacency }),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
execution_mode = match mode {
|
|
||||||
&enums::ExecutionMode::ExecutionModeInputPoints => Some("Points"),
|
|
||||||
&enums::ExecutionMode::ExecutionModeInputLines => Some("Lines"),
|
|
||||||
&enums::ExecutionMode::ExecutionModeInputLinesAdjacency =>
|
|
||||||
Some("LinesWithAdjacency"),
|
|
||||||
&enums::ExecutionMode::ExecutionModeTriangles => Some("Triangles"),
|
|
||||||
&enums::ExecutionMode::ExecutionModeInputTrianglesAdjacency =>
|
|
||||||
Some("TrianglesWithAdjacency"),
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format!(
|
quote!{
|
||||||
"::vulkano::pipeline::shader::GraphicsShaderType::Geometry(
|
::vulkano::pipeline::shader::GraphicsShaderType::Geometry(
|
||||||
\
|
::vulkano::pipeline::shader::GeometryShaderExecutionMode::#execution_mode
|
||||||
::vulkano::pipeline::shader::GeometryShaderExecutionMode::{0}
|
)
|
||||||
\
|
}
|
||||||
)",
|
}
|
||||||
execution_mode.unwrap()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
enums::ExecutionModel::ExecutionModelFragment => {
|
ExecutionModel::ExecutionModelFragment =>
|
||||||
"::vulkano::pipeline::shader::GraphicsShaderType::Fragment".to_owned()
|
quote!{ ::vulkano::pipeline::shader::GraphicsShaderType::Fragment },
|
||||||
},
|
|
||||||
|
|
||||||
enums::ExecutionModel::ExecutionModelGLCompute => {
|
ExecutionModel::ExecutionModelGLCompute => unreachable!(),
|
||||||
unreachable!()
|
|
||||||
},
|
|
||||||
|
|
||||||
enums::ExecutionModel::ExecutionModelKernel => panic!("Kernels are not supported"),
|
ExecutionModel::ExecutionModelKernel => panic!("Kernels are not supported"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stage = match *execution {
|
let stage = match *execution {
|
||||||
enums::ExecutionModel::ExecutionModelVertex => {
|
ExecutionModel::ExecutionModelVertex =>
|
||||||
"ShaderStages { vertex: true, .. ShaderStages::none() }"
|
quote!{ ShaderStages { vertex: true, .. ShaderStages::none() } },
|
||||||
},
|
|
||||||
enums::ExecutionModel::ExecutionModelTessellationControl => {
|
ExecutionModel::ExecutionModelTessellationControl =>
|
||||||
"ShaderStages { tessellation_control: true, .. ShaderStages::none() }"
|
quote!{ ShaderStages { tessellation_control: true, .. ShaderStages::none() } },
|
||||||
},
|
|
||||||
enums::ExecutionModel::ExecutionModelTessellationEvaluation => {
|
ExecutionModel::ExecutionModelTessellationEvaluation =>
|
||||||
"ShaderStages { tessellation_evaluation: true, .. ShaderStages::none() }"
|
quote!{ ShaderStages { tessellation_evaluation: true, .. ShaderStages::none() } },
|
||||||
},
|
|
||||||
enums::ExecutionModel::ExecutionModelGeometry => {
|
ExecutionModel::ExecutionModelGeometry =>
|
||||||
"ShaderStages { geometry: true, .. ShaderStages::none() }"
|
quote!{ ShaderStages { geometry: true, .. ShaderStages::none() } },
|
||||||
},
|
|
||||||
enums::ExecutionModel::ExecutionModelFragment => {
|
ExecutionModel::ExecutionModelFragment =>
|
||||||
"ShaderStages { fragment: true, .. ShaderStages::none() }"
|
quote!{ ShaderStages { fragment: true, .. ShaderStages::none() } },
|
||||||
},
|
|
||||||
enums::ExecutionModel::ExecutionModelGLCompute => unreachable!(),
|
ExecutionModel::ExecutionModelGLCompute => unreachable!(),
|
||||||
enums::ExecutionModel::ExecutionModelKernel => unreachable!(),
|
ExecutionModel::ExecutionModelKernel => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let t = format!("::vulkano::pipeline::shader::GraphicsEntryPoint<{0}, {1}Input, \
|
let mut capitalized_ep_name_input = capitalized_ep_name.clone();
|
||||||
{1}Output, Layout>",
|
capitalized_ep_name_input.push_str("Input");
|
||||||
spec_consts_struct,
|
let capitalized_ep_name_input = Ident::new(&capitalized_ep_name_input, Span::call_site());
|
||||||
capitalized_ep_name);
|
|
||||||
let f = format!("graphics_entry_point(::std::ffi::CStr::from_ptr(NAME.as_ptr() \
|
|
||||||
as *const _), {0}Input, {0}Output, Layout({2}), {1})",
|
|
||||||
capitalized_ep_name,
|
|
||||||
ty,
|
|
||||||
stage);
|
|
||||||
|
|
||||||
(t, f)
|
let mut capitalized_ep_name_output = capitalized_ep_name.clone();
|
||||||
|
capitalized_ep_name_output.push_str("Output");
|
||||||
|
let capitalized_ep_name_output = Ident::new(&capitalized_ep_name_output, Span::call_site());
|
||||||
|
|
||||||
|
let ty = quote!{
|
||||||
|
::vulkano::pipeline::shader::GraphicsEntryPoint<
|
||||||
|
#spec_consts_struct,
|
||||||
|
#capitalized_ep_name_input,
|
||||||
|
#capitalized_ep_name_output,
|
||||||
|
Layout>
|
||||||
|
};
|
||||||
|
let f_call = quote!{
|
||||||
|
graphics_entry_point(
|
||||||
|
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
|
||||||
|
#capitalized_ep_name_input,
|
||||||
|
#capitalized_ep_name_output,
|
||||||
|
Layout(#stage),
|
||||||
|
#entry_ty
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
(ty, f_call)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let entry_point = format!(
|
let mut method_name = ep_name.clone();
|
||||||
r#"
|
method_name.push_str("_entry_point");
|
||||||
/// Returns a logical struct describing the entry point named `{ep_name}`.
|
let method_ident = Ident::new(&method_name, Span::call_site());
|
||||||
#[inline]
|
|
||||||
#[allow(unsafe_code)]
|
let ep_name_lenp1 = ep_name.chars().count() + 1;
|
||||||
pub fn {ep_name}_entry_point(&self) -> {ty} {{
|
let encoded_ep_name = ep_name.chars().map(|c| (c as u8)).collect::<Vec<_>>();
|
||||||
unsafe {{
|
|
||||||
#[allow(dead_code)]
|
let entry_point = quote!{
|
||||||
static NAME: [u8; {ep_name_lenp1}] = [{encoded_ep_name}, 0]; // "{ep_name}"
|
/// Returns a logical struct describing the entry point named `{ep_name}`.
|
||||||
self.shader.{f_call}
|
#[inline]
|
||||||
}}
|
#[allow(unsafe_code)]
|
||||||
}}
|
pub fn #method_ident(&self) -> #ty {
|
||||||
"#,
|
unsafe {
|
||||||
ep_name = ep_name,
|
#[allow(dead_code)]
|
||||||
ep_name_lenp1 = ep_name.chars().count() + 1,
|
static NAME: [u8; #ep_name_lenp1] = [ #( #encoded_ep_name ),* , 0];
|
||||||
ty = ty,
|
self.shader.#f_call
|
||||||
encoded_ep_name = ep_name
|
}
|
||||||
.chars()
|
}
|
||||||
.map(|c| (c as u32).to_string())
|
};
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(", "),
|
|
||||||
f_call = f_call
|
|
||||||
);
|
|
||||||
|
|
||||||
(interface_structs, entry_point)
|
(interface_structs, entry_point)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_interface_structs(doc: &parse::Spirv, capitalized_ep_name: &str, interface: &[u32],
|
struct Element {
|
||||||
|
location: u32,
|
||||||
|
name: String,
|
||||||
|
format: String,
|
||||||
|
location_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_interface_structs(doc: &Spirv, capitalized_ep_name: &str, interface: &[u32],
|
||||||
ignore_first_array_in: bool, ignore_first_array_out: bool)
|
ignore_first_array_in: bool, ignore_first_array_out: bool)
|
||||||
-> String {
|
-> TokenStream {
|
||||||
let mut input_elements = Vec::new();
|
let mut input_elements = vec!();
|
||||||
let mut output_elements = Vec::new();
|
let mut output_elements = vec!();
|
||||||
|
|
||||||
// Filling `input_elements` and `output_elements`.
|
// Filling `input_elements` and `output_elements`.
|
||||||
for interface in interface.iter() {
|
for interface in interface.iter() {
|
||||||
for i in doc.instructions.iter() {
|
for i in doc.instructions.iter() {
|
||||||
match i {
|
match i {
|
||||||
&parse::Instruction::Variable {
|
&Instruction::Variable {
|
||||||
result_type_id,
|
result_type_id,
|
||||||
result_id,
|
result_id,
|
||||||
ref storage_class,
|
ref storage_class,
|
||||||
..
|
..
|
||||||
} if &result_id == interface => {
|
} if &result_id == interface => {
|
||||||
if is_builtin(doc, result_id) {
|
if spirv_search::is_builtin(doc, result_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (to_write, ignore_first_array) = match storage_class {
|
let (to_write, ignore_first_array) = match storage_class {
|
||||||
&enums::StorageClass::StorageClassInput => (&mut input_elements,
|
&StorageClass::StorageClassInput =>
|
||||||
ignore_first_array_in),
|
(&mut input_elements, ignore_first_array_in),
|
||||||
&enums::StorageClass::StorageClassOutput => (&mut output_elements,
|
&StorageClass::StorageClassOutput =>
|
||||||
ignore_first_array_out),
|
(&mut output_elements, ignore_first_array_out),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = name_from_id(doc, result_id);
|
let name = spirv_search::name_from_id(doc, result_id);
|
||||||
if name == "__unnamed" {
|
if name == "__unnamed" {
|
||||||
continue;
|
continue;
|
||||||
} // FIXME: hack
|
} // FIXME: hack
|
||||||
|
|
||||||
let loc = match location_decoration(doc, result_id) {
|
let location = match spirv_search::location_decoration(doc, result_id) {
|
||||||
Some(l) => l,
|
Some(l) => l,
|
||||||
None => panic!("Attribute `{}` (id {}) is missing a location",
|
None => panic!("Attribute `{}` (id {}) is missing a location",
|
||||||
name,
|
name,
|
||||||
result_id),
|
result_id),
|
||||||
};
|
};
|
||||||
|
|
||||||
to_write
|
let (format, location_len) = spirv_search::format_from_id(doc, result_type_id, ignore_first_array);
|
||||||
.push((loc, name, format_from_id(doc, result_type_id, ignore_first_array)));
|
to_write.push(Element { location, name, format, location_len });
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write_interface_struct(&format!("{}Input", capitalized_ep_name), &input_elements) +
|
let input: TokenStream = write_interface_struct(&format!("{}Input", capitalized_ep_name), &input_elements);
|
||||||
&write_interface_struct(&format!("{}Output", capitalized_ep_name), &output_elements)
|
let output: TokenStream = write_interface_struct(&format!("{}Output", capitalized_ep_name), &output_elements);
|
||||||
|
quote!{ #input #output }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_interface_struct(struct_name: &str, attributes: &[(u32, String, (String, usize))])
|
fn write_interface_struct(struct_name_str: &str, attributes: &[Element]) -> TokenStream {
|
||||||
-> String {
|
|
||||||
// Checking for overlapping elements.
|
// Checking for overlapping elements.
|
||||||
for (offset, &(loc, ref name, (_, loc_len))) in attributes.iter().enumerate() {
|
for (offset, element1) in attributes.iter().enumerate() {
|
||||||
for &(loc2, ref name2, (_, loc_len2)) in attributes.iter().skip(offset + 1) {
|
for element2 in attributes.iter().skip(offset + 1) {
|
||||||
if loc == loc2 || (loc < loc2 && loc + loc_len as u32 > loc2) ||
|
if element1.location == element2.location ||
|
||||||
(loc2 < loc && loc2 + loc_len2 as u32 > loc)
|
(element1.location < element2.location && element1.location + element1.location_len as u32 > element2.location) ||
|
||||||
|
(element2.location < element1.location && element2.location + element2.location_len as u32 > element1.location)
|
||||||
{
|
{
|
||||||
panic!("The locations of attributes `{}` (start={}, size={}) \
|
panic!("The locations of attributes `{}` (start={}, size={}) \
|
||||||
and `{}` (start={}, size={}) overlap",
|
and `{}` (start={}, size={}) overlap",
|
||||||
name,
|
element1.name,
|
||||||
loc,
|
element1.location,
|
||||||
loc_len,
|
element1.location_len,
|
||||||
name2,
|
element2.name,
|
||||||
loc2,
|
element2.location,
|
||||||
loc_len2);
|
element2.location_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,73 +264,67 @@ fn write_interface_struct(struct_name: &str, attributes: &[(u32, String, (String
|
|||||||
let body = attributes
|
let body = attributes
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(num, &(loc, ref name, (ref ty, num_locs)))| {
|
.map(|(num, element)| {
|
||||||
assert!(num_locs >= 1);
|
assert!(element.location_len >= 1);
|
||||||
|
let loc = element.location;
|
||||||
|
let loc_end = element.location + element.location_len as u32;
|
||||||
|
let format = Ident::new(&element.format, Span::call_site());
|
||||||
|
let name = &element.name;
|
||||||
|
let num = num as u16;
|
||||||
|
|
||||||
format!(
|
quote!{
|
||||||
"if self.num == {} {{
|
if self.num == #num {
|
||||||
self.num += 1;
|
self.num += 1;
|
||||||
|
|
||||||
return Some(::vulkano::pipeline::shader::ShaderInterfaceDefEntry {{
|
return Some(::vulkano::pipeline::shader::ShaderInterfaceDefEntry {
|
||||||
location: {} .. {},
|
location: #loc .. #loc_end,
|
||||||
format: ::vulkano::format::Format::{},
|
format: ::vulkano::format::Format::#format,
|
||||||
name: Some(::std::borrow::Cow::Borrowed(\"{}\"))
|
name: Some(::std::borrow::Cow::Borrowed(#name))
|
||||||
}});
|
});
|
||||||
}}",
|
}
|
||||||
num,
|
}
|
||||||
loc,
|
|
||||||
loc as usize + num_locs,
|
|
||||||
ty,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>();
|
||||||
.join("");
|
|
||||||
|
|
||||||
format!(
|
let struct_name = Ident::new(struct_name_str, Span::call_site());
|
||||||
"
|
|
||||||
|
let mut iter_name = struct_name.to_string();
|
||||||
|
iter_name.push_str("Iter");
|
||||||
|
let iter_name = Ident::new(&iter_name, Span::call_site());
|
||||||
|
|
||||||
|
let len = attributes.len();
|
||||||
|
|
||||||
|
quote!{
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct {name};
|
pub struct #struct_name;
|
||||||
|
|
||||||
\
|
#[allow(unsafe_code)]
|
||||||
#[allow(unsafe_code)]
|
unsafe impl ::vulkano::pipeline::shader::ShaderInterfaceDef for #struct_name {
|
||||||
unsafe impl ::vulkano::pipeline::shader::ShaderInterfaceDef for \
|
type Iter = #iter_name;
|
||||||
{name} {{
|
fn elements(&self) -> #iter_name {
|
||||||
type Iter = {name}Iter;
|
#iter_name { num: 0 }
|
||||||
fn elements(&self) -> {name}Iter {{
|
}
|
||||||
\
|
}
|
||||||
{name}Iter {{ num: 0 }}
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
\
|
pub struct #iter_name { num: u16 }
|
||||||
pub struct {name}Iter {{ num: u16 }}
|
|
||||||
impl Iterator for {name}Iter {{
|
impl Iterator for #iter_name {
|
||||||
type \
|
type Item = ::vulkano::pipeline::shader::ShaderInterfaceDefEntry;
|
||||||
Item = ::vulkano::pipeline::shader::ShaderInterfaceDefEntry;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
\
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
fn next(&mut self) -> Option<Self::Item> {{
|
#( #body )*
|
||||||
{body}
|
|
||||||
None
|
None
|
||||||
\
|
}
|
||||||
}}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {{
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
\
|
let len = #len - self.num as usize;
|
||||||
let len = ({len} - self.num) as usize;
|
|
||||||
(len, Some(len))
|
(len, Some(len))
|
||||||
}}
|
}
|
||||||
\
|
}
|
||||||
}}
|
|
||||||
|
|
||||||
impl ExactSizeIterator for {name}Iter {{}}
|
impl ExactSizeIterator for #iter_name {}
|
||||||
",
|
}
|
||||||
name = struct_name,
|
|
||||||
body = body,
|
|
||||||
len = attributes.len()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,31 @@
|
|||||||
// notice may not be copied, modified, or distributed except
|
// notice may not be copied, modified, or distributed except
|
||||||
// according to those terms.
|
// according to those terms.
|
||||||
|
|
||||||
extern crate shaderc;
|
#![recursion_limit = "1024"]
|
||||||
|
#[macro_use] extern crate quote;
|
||||||
|
extern crate shaderc;
|
||||||
|
extern crate proc_macro2;
|
||||||
|
extern crate syn;
|
||||||
|
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
|
||||||
|
use syn::Ident;
|
||||||
|
use proc_macro2::{Span, TokenStream};
|
||||||
use shaderc::{Compiler, CompileOptions};
|
use shaderc::{Compiler, CompileOptions};
|
||||||
|
|
||||||
pub use shaderc::{CompilationArtifact, ShaderKind};
|
pub use shaderc::{CompilationArtifact, ShaderKind};
|
||||||
pub use parse::ParseError;
|
pub use parse::ParseError;
|
||||||
|
|
||||||
|
use parse::Instruction;
|
||||||
|
use enums::Capability;
|
||||||
|
|
||||||
mod descriptor_sets;
|
mod descriptor_sets;
|
||||||
mod entry_point;
|
mod entry_point;
|
||||||
mod enums;
|
mod enums;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod spec_consts;
|
mod spec_consts;
|
||||||
mod structs;
|
mod structs;
|
||||||
|
mod spirv_search;
|
||||||
|
|
||||||
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
|
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
|
||||||
let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?;
|
let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?;
|
||||||
@ -34,12 +44,41 @@ pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String
|
|||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reflect(name: &str, spirv: &[u32]) -> Result<String, Error> {
|
pub fn reflect(name: &str, spirv: &[u32]) -> Result<TokenStream, Error> {
|
||||||
|
let struct_name = Ident::new(&name, Span::call_site());
|
||||||
let doc = parse::parse_spirv(spirv)?;
|
let doc = parse::parse_spirv(spirv)?;
|
||||||
|
|
||||||
let mut output = String::new();
|
// checking whether each required capability is enabled in the Vulkan device
|
||||||
output.push_str(
|
let mut cap_checks: Vec<TokenStream> = vec!();
|
||||||
r#"
|
for i in doc.instructions.iter() {
|
||||||
|
if let &Instruction::Capability(ref cap) = i {
|
||||||
|
if let Some(cap_string) = capability_name(cap) {
|
||||||
|
let cap = Ident::new(cap_string, Span::call_site());
|
||||||
|
cap_checks.push(quote!{
|
||||||
|
if !device.enabled_features().#cap {
|
||||||
|
panic!("capability {:?} not enabled", #cap_string); // TODO: error
|
||||||
|
//return Err(CapabilityNotEnabled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writing one method for each entry point of this module
|
||||||
|
let mut entry_points_inside_impl: Vec<TokenStream> = vec!();
|
||||||
|
let mut entry_points_outside_impl: Vec<TokenStream> = vec!();
|
||||||
|
for instruction in doc.instructions.iter() {
|
||||||
|
if let &Instruction::EntryPoint { .. } = instruction {
|
||||||
|
let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction);
|
||||||
|
entry_points_inside_impl.push(entry_point);
|
||||||
|
entry_points_outside_impl.push(outside);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let structs = structs::write_structs(&doc);
|
||||||
|
let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc);
|
||||||
|
let specialization_constants = spec_consts::write_specialization_constants(&doc);
|
||||||
|
let ast = quote!{
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -77,106 +116,51 @@ pub fn reflect(name: &str, spirv: &[u32]) -> Result<String, Error> {
|
|||||||
use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait;
|
use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use vulkano::pipeline::shader::SpecializationMapEntry;
|
use vulkano::pipeline::shader::SpecializationMapEntry;
|
||||||
"#,
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
pub struct #struct_name {
|
||||||
// contains the data that was passed as input to this function
|
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
|
||||||
let spirv_words = spirv.iter()
|
}
|
||||||
.map(|&word| word.to_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(", ");
|
|
||||||
|
|
||||||
// writing the header
|
impl #struct_name {
|
||||||
output.push_str(&format!(
|
/// Loads the shader in Vulkan as a `ShaderModule`.
|
||||||
r#"
|
#[inline]
|
||||||
pub struct {name} {{
|
#[allow(unsafe_code)]
|
||||||
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
|
pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>)
|
||||||
}}
|
-> Result<#struct_name, ::vulkano::OomError>
|
||||||
|
{
|
||||||
|
#( #cap_checks )*
|
||||||
|
let words = [ #( #spirv ),* ];
|
||||||
|
|
||||||
impl {name} {{
|
unsafe {
|
||||||
/// Loads the shader in Vulkan as a `ShaderModule`.
|
Ok(#struct_name {
|
||||||
#[inline]
|
shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
|
||||||
#[allow(unsafe_code)]
|
})
|
||||||
pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>)
|
|
||||||
-> Result<{name}, ::vulkano::OomError>
|
|
||||||
{{
|
|
||||||
|
|
||||||
"#,
|
|
||||||
name = name
|
|
||||||
));
|
|
||||||
|
|
||||||
// checking whether each required capability is enabled in the Vulkan device
|
|
||||||
for i in doc.instructions.iter() {
|
|
||||||
if let &parse::Instruction::Capability(ref cap) = i {
|
|
||||||
if let Some(cap) = capability_name(cap) {
|
|
||||||
output.push_str(&format!(
|
|
||||||
r#"
|
|
||||||
if !device.enabled_features().{cap} {{
|
|
||||||
panic!("capability {{:?}} not enabled", "{cap}") // FIXME: error
|
|
||||||
//return Err(CapabilityNotEnabled);
|
|
||||||
}}"#,
|
|
||||||
cap = cap
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// follow-up of the header
|
/// Returns the module that was created.
|
||||||
output.push_str(&format!(
|
#[allow(dead_code)]
|
||||||
r#"
|
#[inline]
|
||||||
unsafe {{
|
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {
|
||||||
let words = [{spirv_words}];
|
&self.shader
|
||||||
|
|
||||||
Ok({name} {{
|
|
||||||
shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
|
|
||||||
}})
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
|
|
||||||
/// Returns the module that was created.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline]
|
|
||||||
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {{
|
|
||||||
&self.shader
|
|
||||||
}}
|
|
||||||
"#,
|
|
||||||
name = name,
|
|
||||||
spirv_words = spirv_words
|
|
||||||
));
|
|
||||||
|
|
||||||
// writing one method for each entry point of this module
|
|
||||||
let mut outside_impl = String::new();
|
|
||||||
for instruction in doc.instructions.iter() {
|
|
||||||
if let &parse::Instruction::EntryPoint { .. } = instruction {
|
|
||||||
let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction);
|
|
||||||
output.push_str(&entry_point);
|
|
||||||
outside_impl.push_str(&outside);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#( #entry_points_inside_impl )*
|
||||||
}
|
}
|
||||||
|
|
||||||
// footer
|
#( #entry_points_outside_impl )*
|
||||||
output.push_str(&format!(
|
|
||||||
r#"
|
|
||||||
}}
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
output.push_str(&outside_impl);
|
pub mod ty {
|
||||||
|
#structs
|
||||||
|
}
|
||||||
|
|
||||||
// struct definitions
|
#descriptor_sets
|
||||||
output.push_str("pub mod ty {");
|
#specialization_constants
|
||||||
output.push_str(&structs::write_structs(&doc));
|
};
|
||||||
output.push_str("}");
|
|
||||||
|
|
||||||
// descriptor sets
|
//println!("{}", ast.to_string());
|
||||||
output.push_str(&descriptor_sets::write_descriptor_sets(&doc));
|
|
||||||
|
|
||||||
// specialization constants
|
Ok(ast)
|
||||||
output.push_str(&spec_consts::write_specialization_constants(&doc));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -199,301 +183,78 @@ impl From<ParseError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the vulkano `Format` and number of occupied locations from an id.
|
|
||||||
///
|
|
||||||
/// If `ignore_first_array` is true, the function expects the outermost instruction to be
|
|
||||||
/// `OpTypeArray`. If it's the case, the OpTypeArray will be ignored. If not, the function will
|
|
||||||
/// panic.
|
|
||||||
fn format_from_id(doc: &parse::Spirv, searched: u32, ignore_first_array: bool) -> (String, usize) {
|
|
||||||
for instruction in doc.instructions.iter() {
|
|
||||||
match instruction {
|
|
||||||
&parse::Instruction::TypeInt {
|
|
||||||
result_id,
|
|
||||||
width,
|
|
||||||
signedness,
|
|
||||||
} if result_id == searched => {
|
|
||||||
assert!(!ignore_first_array);
|
|
||||||
return (match (width, signedness) {
|
|
||||||
(8, true) => "R8Sint",
|
|
||||||
(8, false) => "R8Uint",
|
|
||||||
(16, true) => "R16Sint",
|
|
||||||
(16, false) => "R16Uint",
|
|
||||||
(32, true) => "R32Sint",
|
|
||||||
(32, false) => "R32Uint",
|
|
||||||
(64, true) => "R64Sint",
|
|
||||||
(64, false) => "R64Uint",
|
|
||||||
_ => panic!(),
|
|
||||||
}.to_owned(),
|
|
||||||
1);
|
|
||||||
},
|
|
||||||
&parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
|
|
||||||
assert!(!ignore_first_array);
|
|
||||||
return (match width {
|
|
||||||
32 => "R32Sfloat",
|
|
||||||
64 => "R64Sfloat",
|
|
||||||
_ => panic!(),
|
|
||||||
}.to_owned(),
|
|
||||||
1);
|
|
||||||
},
|
|
||||||
&parse::Instruction::TypeVector {
|
|
||||||
result_id,
|
|
||||||
component_id,
|
|
||||||
count,
|
|
||||||
} if result_id == searched => {
|
|
||||||
assert!(!ignore_first_array);
|
|
||||||
let (format, sz) = format_from_id(doc, component_id, false);
|
|
||||||
assert!(format.starts_with("R32"));
|
|
||||||
assert_eq!(sz, 1);
|
|
||||||
let format = if count == 1 {
|
|
||||||
format
|
|
||||||
} else if count == 2 {
|
|
||||||
format!("R32G32{}", &format[3 ..])
|
|
||||||
} else if count == 3 {
|
|
||||||
format!("R32G32B32{}", &format[3 ..])
|
|
||||||
} else if count == 4 {
|
|
||||||
format!("R32G32B32A32{}", &format[3 ..])
|
|
||||||
} else {
|
|
||||||
panic!("Found vector type with more than 4 elements")
|
|
||||||
};
|
|
||||||
return (format, sz);
|
|
||||||
},
|
|
||||||
&parse::Instruction::TypeMatrix {
|
|
||||||
result_id,
|
|
||||||
column_type_id,
|
|
||||||
column_count,
|
|
||||||
} if result_id == searched => {
|
|
||||||
assert!(!ignore_first_array);
|
|
||||||
let (format, sz) = format_from_id(doc, column_type_id, false);
|
|
||||||
return (format, sz * column_count as usize);
|
|
||||||
},
|
|
||||||
&parse::Instruction::TypeArray {
|
|
||||||
result_id,
|
|
||||||
type_id,
|
|
||||||
length_id,
|
|
||||||
} if result_id == searched => {
|
|
||||||
if ignore_first_array {
|
|
||||||
return format_from_id(doc, type_id, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (format, sz) = format_from_id(doc, type_id, false);
|
|
||||||
let len = doc.instructions
|
|
||||||
.iter()
|
|
||||||
.filter_map(|e| match e {
|
|
||||||
&parse::Instruction::Constant {
|
|
||||||
result_id,
|
|
||||||
ref data,
|
|
||||||
..
|
|
||||||
} if result_id == length_id => Some(data.clone()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
.expect("failed to find array length");
|
|
||||||
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
|
|
||||||
return (format, sz * len as usize);
|
|
||||||
},
|
|
||||||
&parse::Instruction::TypePointer { result_id, type_id, .. }
|
|
||||||
if result_id == searched => {
|
|
||||||
return format_from_id(doc, type_id, ignore_first_array);
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
panic!("Type #{} not found or invalid", searched)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name_from_id(doc: &parse::Spirv, searched: u32) -> String {
|
|
||||||
doc.instructions
|
|
||||||
.iter()
|
|
||||||
.filter_map(|i| if let &parse::Instruction::Name {
|
|
||||||
target_id,
|
|
||||||
ref name,
|
|
||||||
} = i
|
|
||||||
{
|
|
||||||
if target_id == searched {
|
|
||||||
Some(name.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
.and_then(|n| if !n.is_empty() { Some(n) } else { None })
|
|
||||||
.unwrap_or("__unnamed".to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn member_name_from_id(doc: &parse::Spirv, searched: u32, searched_member: u32) -> String {
|
|
||||||
doc.instructions
|
|
||||||
.iter()
|
|
||||||
.filter_map(|i| if let &parse::Instruction::MemberName {
|
|
||||||
target_id,
|
|
||||||
member,
|
|
||||||
ref name,
|
|
||||||
} = i
|
|
||||||
{
|
|
||||||
if target_id == searched && member == searched_member {
|
|
||||||
Some(name.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
.and_then(|n| if !n.is_empty() { Some(n) } else { None })
|
|
||||||
.unwrap_or("__unnamed".to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn location_decoration(doc: &parse::Spirv, searched: u32) -> Option<u32> {
|
|
||||||
doc.instructions
|
|
||||||
.iter()
|
|
||||||
.filter_map(|i| if let &parse::Instruction::Decorate {
|
|
||||||
target_id,
|
|
||||||
decoration: enums::Decoration::DecorationLocation,
|
|
||||||
ref params,
|
|
||||||
} = i
|
|
||||||
{
|
|
||||||
if target_id == searched {
|
|
||||||
Some(params[0])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if a `BuiltIn` decorator is applied on an id.
|
|
||||||
fn is_builtin(doc: &parse::Spirv, id: u32) -> bool {
|
|
||||||
for instruction in &doc.instructions {
|
|
||||||
match *instruction {
|
|
||||||
parse::Instruction::Decorate {
|
|
||||||
target_id,
|
|
||||||
decoration: enums::Decoration::DecorationBuiltIn,
|
|
||||||
..
|
|
||||||
} if target_id == id => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
parse::Instruction::MemberDecorate {
|
|
||||||
target_id,
|
|
||||||
decoration: enums::Decoration::DecorationBuiltIn,
|
|
||||||
..
|
|
||||||
} if target_id == id => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for instruction in &doc.instructions {
|
|
||||||
match *instruction {
|
|
||||||
parse::Instruction::Variable {
|
|
||||||
result_type_id,
|
|
||||||
result_id,
|
|
||||||
..
|
|
||||||
} if result_id == id => {
|
|
||||||
return is_builtin(doc, result_type_id);
|
|
||||||
},
|
|
||||||
parse::Instruction::TypeArray { result_id, type_id, .. } if result_id == id => {
|
|
||||||
return is_builtin(doc, type_id);
|
|
||||||
},
|
|
||||||
parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == id => {
|
|
||||||
return is_builtin(doc, type_id);
|
|
||||||
},
|
|
||||||
parse::Instruction::TypeStruct {
|
|
||||||
result_id,
|
|
||||||
ref member_types,
|
|
||||||
} if result_id == id => {
|
|
||||||
for &mem in member_types {
|
|
||||||
if is_builtin(doc, mem) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parse::Instruction::TypePointer { result_id, type_id, .. } if result_id == id => {
|
|
||||||
return is_builtin(doc, type_id);
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the name of the Vulkan something that corresponds to an `OpCapability`.
|
/// Returns the name of the Vulkan something that corresponds to an `OpCapability`.
|
||||||
///
|
///
|
||||||
/// Returns `None` if irrelevant.
|
/// Returns `None` if irrelevant.
|
||||||
// TODO: this function is a draft, as the actual names may not be the same
|
// TODO: this function is a draft, as the actual names may not be the same
|
||||||
fn capability_name(cap: &enums::Capability) -> Option<&'static str> {
|
fn capability_name(cap: &Capability) -> Option<&'static str> {
|
||||||
match *cap {
|
match *cap {
|
||||||
enums::Capability::CapabilityMatrix => None, // always supported
|
Capability::CapabilityMatrix => None, // always supported
|
||||||
enums::Capability::CapabilityShader => None, // always supported
|
Capability::CapabilityShader => None, // always supported
|
||||||
enums::Capability::CapabilityGeometry => Some("geometry_shader"),
|
Capability::CapabilityGeometry => Some("geometry_shader"),
|
||||||
enums::Capability::CapabilityTessellation => Some("tessellation_shader"),
|
Capability::CapabilityTessellation => Some("tessellation_shader"),
|
||||||
enums::Capability::CapabilityAddresses => panic!(), // not supported
|
Capability::CapabilityAddresses => panic!(), // not supported
|
||||||
enums::Capability::CapabilityLinkage => panic!(), // not supported
|
Capability::CapabilityLinkage => panic!(), // not supported
|
||||||
enums::Capability::CapabilityKernel => panic!(), // not supported
|
Capability::CapabilityKernel => panic!(), // not supported
|
||||||
enums::Capability::CapabilityVector16 => panic!(), // not supported
|
Capability::CapabilityVector16 => panic!(), // not supported
|
||||||
enums::Capability::CapabilityFloat16Buffer => panic!(), // not supported
|
Capability::CapabilityFloat16Buffer => panic!(), // not supported
|
||||||
enums::Capability::CapabilityFloat16 => panic!(), // not supported
|
Capability::CapabilityFloat16 => panic!(), // not supported
|
||||||
enums::Capability::CapabilityFloat64 => Some("shader_f3264"),
|
Capability::CapabilityFloat64 => Some("shader_f3264"),
|
||||||
enums::Capability::CapabilityInt64 => Some("shader_int64"),
|
Capability::CapabilityInt64 => Some("shader_int64"),
|
||||||
enums::Capability::CapabilityInt64Atomics => panic!(), // not supported
|
Capability::CapabilityInt64Atomics => panic!(), // not supported
|
||||||
enums::Capability::CapabilityImageBasic => panic!(), // not supported
|
Capability::CapabilityImageBasic => panic!(), // not supported
|
||||||
enums::Capability::CapabilityImageReadWrite => panic!(), // not supported
|
Capability::CapabilityImageReadWrite => panic!(), // not supported
|
||||||
enums::Capability::CapabilityImageMipmap => panic!(), // not supported
|
Capability::CapabilityImageMipmap => panic!(), // not supported
|
||||||
enums::Capability::CapabilityPipes => panic!(), // not supported
|
Capability::CapabilityPipes => panic!(), // not supported
|
||||||
enums::Capability::CapabilityGroups => panic!(), // not supported
|
Capability::CapabilityGroups => panic!(), // not supported
|
||||||
enums::Capability::CapabilityDeviceEnqueue => panic!(), // not supported
|
Capability::CapabilityDeviceEnqueue => panic!(), // not supported
|
||||||
enums::Capability::CapabilityLiteralSampler => panic!(), // not supported
|
Capability::CapabilityLiteralSampler => panic!(), // not supported
|
||||||
enums::Capability::CapabilityAtomicStorage => panic!(), // not supported
|
Capability::CapabilityAtomicStorage => panic!(), // not supported
|
||||||
enums::Capability::CapabilityInt16 => Some("shader_int16"),
|
Capability::CapabilityInt16 => Some("shader_int16"),
|
||||||
enums::Capability::CapabilityTessellationPointSize =>
|
Capability::CapabilityTessellationPointSize =>
|
||||||
Some("shader_tessellation_and_geometry_point_size"),
|
Some("shader_tessellation_and_geometry_point_size"),
|
||||||
enums::Capability::CapabilityGeometryPointSize =>
|
Capability::CapabilityGeometryPointSize =>
|
||||||
Some("shader_tessellation_and_geometry_point_size"),
|
Some("shader_tessellation_and_geometry_point_size"),
|
||||||
enums::Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"),
|
Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"),
|
||||||
enums::Capability::CapabilityStorageImageMultisample =>
|
Capability::CapabilityStorageImageMultisample =>
|
||||||
Some("shader_storage_image_multisample"),
|
Some("shader_storage_image_multisample"),
|
||||||
enums::Capability::CapabilityUniformBufferArrayDynamicIndexing =>
|
Capability::CapabilityUniformBufferArrayDynamicIndexing =>
|
||||||
Some("shader_uniform_buffer_array_dynamic_indexing"),
|
Some("shader_uniform_buffer_array_dynamic_indexing"),
|
||||||
enums::Capability::CapabilitySampledImageArrayDynamicIndexing =>
|
Capability::CapabilitySampledImageArrayDynamicIndexing =>
|
||||||
Some("shader_sampled_image_array_dynamic_indexing"),
|
Some("shader_sampled_image_array_dynamic_indexing"),
|
||||||
enums::Capability::CapabilityStorageBufferArrayDynamicIndexing =>
|
Capability::CapabilityStorageBufferArrayDynamicIndexing =>
|
||||||
Some("shader_storage_buffer_array_dynamic_indexing"),
|
Some("shader_storage_buffer_array_dynamic_indexing"),
|
||||||
enums::Capability::CapabilityStorageImageArrayDynamicIndexing =>
|
Capability::CapabilityStorageImageArrayDynamicIndexing =>
|
||||||
Some("shader_storage_image_array_dynamic_indexing"),
|
Some("shader_storage_image_array_dynamic_indexing"),
|
||||||
enums::Capability::CapabilityClipDistance => Some("shader_clip_distance"),
|
Capability::CapabilityClipDistance => Some("shader_clip_distance"),
|
||||||
enums::Capability::CapabilityCullDistance => Some("shader_cull_distance"),
|
Capability::CapabilityCullDistance => Some("shader_cull_distance"),
|
||||||
enums::Capability::CapabilityImageCubeArray => Some("image_cube_array"),
|
Capability::CapabilityImageCubeArray => Some("image_cube_array"),
|
||||||
enums::Capability::CapabilitySampleRateShading => Some("sample_rate_shading"),
|
Capability::CapabilitySampleRateShading => Some("sample_rate_shading"),
|
||||||
enums::Capability::CapabilityImageRect => panic!(), // not supported
|
Capability::CapabilityImageRect => panic!(), // not supported
|
||||||
enums::Capability::CapabilitySampledRect => panic!(), // not supported
|
Capability::CapabilitySampledRect => panic!(), // not supported
|
||||||
enums::Capability::CapabilityGenericPointer => panic!(), // not supported
|
Capability::CapabilityGenericPointer => panic!(), // not supported
|
||||||
enums::Capability::CapabilityInt8 => panic!(), // not supported
|
Capability::CapabilityInt8 => panic!(), // not supported
|
||||||
enums::Capability::CapabilityInputAttachment => None, // always supported
|
Capability::CapabilityInputAttachment => None, // always supported
|
||||||
enums::Capability::CapabilitySparseResidency => Some("shader_resource_residency"),
|
Capability::CapabilitySparseResidency => Some("shader_resource_residency"),
|
||||||
enums::Capability::CapabilityMinLod => Some("shader_resource_min_lod"),
|
Capability::CapabilityMinLod => Some("shader_resource_min_lod"),
|
||||||
enums::Capability::CapabilitySampled1D => None, // always supported
|
Capability::CapabilitySampled1D => None, // always supported
|
||||||
enums::Capability::CapabilityImage1D => None, // always supported
|
Capability::CapabilityImage1D => None, // always supported
|
||||||
enums::Capability::CapabilitySampledCubeArray => Some("image_cube_array"),
|
Capability::CapabilitySampledCubeArray => Some("image_cube_array"),
|
||||||
enums::Capability::CapabilitySampledBuffer => None, // always supported
|
Capability::CapabilitySampledBuffer => None, // always supported
|
||||||
enums::Capability::CapabilityImageBuffer => None, // always supported
|
Capability::CapabilityImageBuffer => None, // always supported
|
||||||
enums::Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"),
|
Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"),
|
||||||
enums::Capability::CapabilityStorageImageExtendedFormats =>
|
Capability::CapabilityStorageImageExtendedFormats =>
|
||||||
Some("shader_storage_image_extended_formats"),
|
Some("shader_storage_image_extended_formats"),
|
||||||
enums::Capability::CapabilityImageQuery => None, // always supported
|
Capability::CapabilityImageQuery => None, // always supported
|
||||||
enums::Capability::CapabilityDerivativeControl => None, // always supported
|
Capability::CapabilityDerivativeControl => None, // always supported
|
||||||
enums::Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"),
|
Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"),
|
||||||
enums::Capability::CapabilityTransformFeedback => panic!(), // not supported
|
Capability::CapabilityTransformFeedback => panic!(), // not supported
|
||||||
enums::Capability::CapabilityGeometryStreams => panic!(), // not supported
|
Capability::CapabilityGeometryStreams => panic!(), // not supported
|
||||||
enums::Capability::CapabilityStorageImageReadWithoutFormat =>
|
Capability::CapabilityStorageImageReadWithoutFormat =>
|
||||||
Some("shader_storage_image_read_without_format"),
|
Some("shader_storage_image_read_without_format"),
|
||||||
enums::Capability::CapabilityStorageImageWriteWithoutFormat =>
|
Capability::CapabilityStorageImageWriteWithoutFormat =>
|
||||||
Some("shader_storage_image_write_without_format"),
|
Some("shader_storage_image_write_without_format"),
|
||||||
enums::Capability::CapabilityMultiViewport => Some("multi_viewport"),
|
Capability::CapabilityMultiViewport => Some("multi_viewport"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,17 +9,22 @@
|
|||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use enums;
|
use syn::Ident;
|
||||||
use parse;
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
|
||||||
|
use enums::Decoration;
|
||||||
|
use parse::{Instruction, Spirv};
|
||||||
|
use spirv_search;
|
||||||
|
use structs;
|
||||||
|
|
||||||
/// Returns true if the document has specialization constants.
|
/// Returns true if the document has specialization constants.
|
||||||
pub fn has_specialization_constants(doc: &parse::Spirv) -> bool {
|
pub fn has_specialization_constants(doc: &Spirv) -> bool {
|
||||||
for instruction in doc.instructions.iter() {
|
for instruction in doc.instructions.iter() {
|
||||||
match instruction {
|
match instruction {
|
||||||
&parse::Instruction::SpecConstantTrue { .. } => return true,
|
&Instruction::SpecConstantTrue { .. } => return true,
|
||||||
&parse::Instruction::SpecConstantFalse { .. } => return true,
|
&Instruction::SpecConstantFalse { .. } => return true,
|
||||||
&parse::Instruction::SpecConstant { .. } => return true,
|
&Instruction::SpecConstant { .. } => return true,
|
||||||
&parse::Instruction::SpecConstantComposite { .. } => return true,
|
&Instruction::SpecConstantComposite { .. } => return true,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,56 +34,38 @@ pub fn has_specialization_constants(doc: &parse::Spirv) -> bool {
|
|||||||
|
|
||||||
/// Writes the `SpecializationConstants` struct that contains the specialization constants and
|
/// Writes the `SpecializationConstants` struct that contains the specialization constants and
|
||||||
/// implements the `Default` and the `vulkano::pipeline::shader::SpecializationConstants` traits.
|
/// implements the `Default` and the `vulkano::pipeline::shader::SpecializationConstants` traits.
|
||||||
pub fn write_specialization_constants(doc: &parse::Spirv) -> String {
|
pub fn write_specialization_constants(doc: &Spirv) -> TokenStream {
|
||||||
struct SpecConst {
|
struct SpecConst {
|
||||||
name: String,
|
name: String,
|
||||||
constant_id: u32,
|
constant_id: u32,
|
||||||
rust_ty: String,
|
rust_ty: TokenStream,
|
||||||
rust_size: usize,
|
rust_size: usize,
|
||||||
rust_alignment: usize,
|
rust_alignment: u32,
|
||||||
default_value: String,
|
default_value: TokenStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut spec_consts = Vec::new();
|
let mut spec_consts = Vec::new();
|
||||||
|
|
||||||
for instruction in doc.instructions.iter() {
|
for instruction in doc.instructions.iter() {
|
||||||
let (type_id, result_id, default_value) = match instruction {
|
let (type_id, result_id, default_value) = match instruction {
|
||||||
&parse::Instruction::SpecConstantTrue {
|
&Instruction::SpecConstantTrue { result_type_id, result_id } =>
|
||||||
result_type_id,
|
(result_type_id, result_id, quote!{1u32}),
|
||||||
result_id,
|
|
||||||
} => {
|
&Instruction::SpecConstantFalse { result_type_id, result_id } =>
|
||||||
(result_type_id, result_id, "1u32".to_string())
|
(result_type_id, result_id, quote!{0u32}),
|
||||||
},
|
|
||||||
&parse::Instruction::SpecConstantFalse {
|
&Instruction::SpecConstant { result_type_id, result_id, ref data } => {
|
||||||
result_type_id,
|
let def_val = quote!{
|
||||||
result_id,
|
unsafe {{ ::std::mem::transmute([ #( #data ),* ]) }}
|
||||||
} => {
|
};
|
||||||
(result_type_id, result_id, "0u32".to_string())
|
|
||||||
},
|
|
||||||
&parse::Instruction::SpecConstant {
|
|
||||||
result_type_id,
|
|
||||||
result_id,
|
|
||||||
ref data,
|
|
||||||
} => {
|
|
||||||
let data = data.iter()
|
|
||||||
.map(|d| d.to_string() + "u32")
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ");
|
|
||||||
let def_val = format!("unsafe {{ ::std::mem::transmute([{}]) }}", data);
|
|
||||||
(result_type_id, result_id, def_val)
|
(result_type_id, result_id, def_val)
|
||||||
},
|
}
|
||||||
&parse::Instruction::SpecConstantComposite {
|
&Instruction::SpecConstantComposite { result_type_id, result_id, ref data } => {
|
||||||
result_type_id,
|
let def_val = quote!{
|
||||||
result_id,
|
unsafe {{ ::std::mem::transmute([ #( #data ),* ]) }}
|
||||||
ref data,
|
};
|
||||||
} => {
|
|
||||||
let data = data.iter()
|
|
||||||
.map(|d| d.to_string() + "u32")
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ");
|
|
||||||
let def_val = format!("unsafe {{ ::std::mem::transmute([{}]) }}", data);
|
|
||||||
(result_type_id, result_id, def_val)
|
(result_type_id, result_id, def_val)
|
||||||
},
|
}
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,105 +75,93 @@ pub fn write_specialization_constants(doc: &parse::Spirv) -> String {
|
|||||||
let constant_id = doc.instructions
|
let constant_id = doc.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|i| match i {
|
.filter_map(|i| match i {
|
||||||
&parse::Instruction::Decorate {
|
&Instruction::Decorate { target_id, decoration: Decoration::DecorationSpecId, ref params }
|
||||||
target_id,
|
if target_id == result_id => Some(params[0]),
|
||||||
decoration: enums::Decoration::DecorationSpecId,
|
_ => None,
|
||||||
ref params,
|
})
|
||||||
} if target_id == result_id => {
|
|
||||||
Some(params[0])
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.next()
|
.next()
|
||||||
.expect("Found a specialization constant with no SpecId decoration");
|
.expect("Found a specialization constant with no SpecId decoration");
|
||||||
|
|
||||||
spec_consts.push(SpecConst {
|
spec_consts.push(SpecConst {
|
||||||
name: ::name_from_id(doc, result_id),
|
name: spirv_search::name_from_id(doc, result_id),
|
||||||
constant_id,
|
constant_id,
|
||||||
rust_ty,
|
rust_ty,
|
||||||
rust_size,
|
rust_size,
|
||||||
rust_alignment,
|
rust_alignment: rust_alignment as u32,
|
||||||
default_value,
|
default_value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let map_entries = {
|
let map_entries = {
|
||||||
let mut map_entries = Vec::new();
|
let mut map_entries = Vec::new();
|
||||||
let mut curr_offset = 0;
|
let mut curr_offset = 0;
|
||||||
for c in &spec_consts {
|
for spec_const in &spec_consts {
|
||||||
map_entries.push(format!(
|
let constant_id = spec_const.constant_id;
|
||||||
"SpecializationMapEntry {{
|
let rust_size = spec_const.rust_size;
|
||||||
constant_id: \
|
map_entries.push(quote!{
|
||||||
{},
|
SpecializationMapEntry {
|
||||||
offset: {},
|
constant_id: #constant_id,
|
||||||
size: {},
|
offset: #curr_offset,
|
||||||
\
|
size: #rust_size,
|
||||||
}}",
|
}
|
||||||
c.constant_id,
|
});
|
||||||
curr_offset,
|
|
||||||
c.rust_size
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_ne!(c.rust_size, 0);
|
assert_ne!(spec_const.rust_size, 0);
|
||||||
curr_offset += c.rust_size;
|
curr_offset += spec_const.rust_size as u32;
|
||||||
curr_offset = c.rust_alignment * (1 + (curr_offset - 1) / c.rust_alignment);
|
curr_offset = spec_const.rust_alignment * (1 + (curr_offset - 1) / spec_const.rust_alignment);
|
||||||
}
|
}
|
||||||
map_entries
|
map_entries
|
||||||
};
|
};
|
||||||
|
|
||||||
format!(
|
let num_map_entries = map_entries.len();
|
||||||
r#"
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
let mut struct_members = vec!();
|
||||||
#[allow(non_snake_case)]
|
let mut struct_member_defaults = vec!();
|
||||||
#[repr(C)]
|
for spec_const in spec_consts {
|
||||||
pub struct SpecializationConstants {{
|
let name = Ident::new(&spec_const.name, Span::call_site());
|
||||||
{struct_def}
|
let rust_ty = spec_const.rust_ty;
|
||||||
}}
|
let default_value = spec_const.default_value;
|
||||||
|
struct_members.push(quote!{ pub #name: #rust_ty });
|
||||||
|
struct_member_defaults.push(quote!{ #name: #default_value });
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for SpecializationConstants {{
|
quote!{
|
||||||
fn default() -> SpecializationConstants {{
|
#[derive(Debug, Copy, Clone)]
|
||||||
SpecializationConstants {{
|
#[allow(non_snake_case)]
|
||||||
{def_vals}
|
#[repr(C)]
|
||||||
}}
|
pub struct SpecializationConstants {
|
||||||
}}
|
#( #struct_members ),*
|
||||||
}}
|
}
|
||||||
|
|
||||||
unsafe impl SpecConstsTrait for SpecializationConstants {{
|
impl Default for SpecializationConstants {
|
||||||
fn descriptors() -> &'static [SpecializationMapEntry] {{
|
fn default() -> SpecializationConstants {
|
||||||
static DESCRIPTORS: [SpecializationMapEntry; {num_map_entries}] = [
|
SpecializationConstants {
|
||||||
{map_entries}
|
#( #struct_member_defaults ),*
|
||||||
];
|
}
|
||||||
&DESCRIPTORS
|
}
|
||||||
}}
|
}
|
||||||
}}
|
|
||||||
|
|
||||||
"#,
|
unsafe impl SpecConstsTrait for SpecializationConstants {
|
||||||
struct_def = spec_consts
|
fn descriptors() -> &'static [SpecializationMapEntry] {
|
||||||
.iter()
|
static DESCRIPTORS: [SpecializationMapEntry; #num_map_entries] = [
|
||||||
.map(|c| format!("pub {}: {}", c.name, c.rust_ty))
|
#( #map_entries ),*
|
||||||
.collect::<Vec<_>>()
|
];
|
||||||
.join(", "),
|
&DESCRIPTORS
|
||||||
def_vals = spec_consts
|
}
|
||||||
.iter()
|
}
|
||||||
.map(|c| format!("{}: {}", c.name, c.default_value))
|
}
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", "),
|
|
||||||
num_map_entries = map_entries.len(),
|
|
||||||
map_entries = map_entries.join(", ")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper around `type_from_id` that also handles booleans.
|
// Wrapper around `type_from_id` that also handles booleans.
|
||||||
fn spec_const_type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
|
fn spec_const_type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>, usize) {
|
||||||
for instruction in doc.instructions.iter() {
|
for instruction in doc.instructions.iter() {
|
||||||
match instruction {
|
match instruction {
|
||||||
&parse::Instruction::TypeBool { result_id } if result_id == searched => {
|
&Instruction::TypeBool { result_id } if result_id == searched => {
|
||||||
return ("u32".to_owned(), Some(mem::size_of::<u32>()), mem::align_of::<u32>());
|
return (quote!{u32}, Some(mem::size_of::<u32>()), mem::align_of::<u32>());
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::structs::type_from_id(doc, searched)
|
structs::type_from_id(doc, searched)
|
||||||
}
|
}
|
||||||
|
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 std::mem;
|
||||||
|
|
||||||
use enums;
|
use syn::Ident;
|
||||||
use parse;
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
|
||||||
|
use parse::{Instruction, Spirv};
|
||||||
|
use enums::Decoration;
|
||||||
|
use spirv_search;
|
||||||
|
|
||||||
/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
|
/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
|
||||||
pub fn write_structs(doc: &parse::Spirv) -> String {
|
pub fn write_structs(doc: &Spirv) -> TokenStream {
|
||||||
let mut result = String::new();
|
let mut structs = vec!();
|
||||||
|
|
||||||
for instruction in &doc.instructions {
|
for instruction in &doc.instructions {
|
||||||
match *instruction {
|
match *instruction {
|
||||||
parse::Instruction::TypeStruct {
|
Instruction::TypeStruct { result_id, ref member_types } =>
|
||||||
result_id,
|
structs.push(write_struct(doc, result_id, member_types).0),
|
||||||
ref member_types,
|
_ => ()
|
||||||
} => {
|
|
||||||
let (s, _) = write_struct(doc, result_id, member_types);
|
|
||||||
result.push_str(&s);
|
|
||||||
result.push_str("\n");
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
quote!{
|
||||||
}
|
#( #structs )*
|
||||||
|
|
||||||
/// Represents a rust struct member
|
|
||||||
struct Member {
|
|
||||||
name: String,
|
|
||||||
value: String,
|
|
||||||
offset: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Member {
|
|
||||||
fn declaration_text(&self) -> String {
|
|
||||||
let offset = match self.offset {
|
|
||||||
Some(o) => format!("/* offset: {} */", o),
|
|
||||||
_ => "".to_owned(),
|
|
||||||
};
|
|
||||||
format!(" pub {}: {} {}", self.name, self.value, offset)
|
|
||||||
}
|
|
||||||
fn copy_text(&self) -> String {
|
|
||||||
format!(" {name}: self.{name}", name = self.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Analyzes a single struct, returns a string containing its Rust definition, plus its size.
|
/// Analyzes a single struct, returns a string containing its Rust definition, plus its size.
|
||||||
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String, Option<usize>) {
|
fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, Option<usize>) {
|
||||||
let name = ::name_from_id(doc, struct_id);
|
let name = Ident::new(&spirv_search::name_from_id(doc, struct_id), Span::call_site());
|
||||||
|
|
||||||
// The members of this struct.
|
// The members of this struct.
|
||||||
|
struct Member {
|
||||||
|
pub name: Ident,
|
||||||
|
pub ty: TokenStream,
|
||||||
|
}
|
||||||
let mut rust_members = Vec::with_capacity(members.len());
|
let mut rust_members = Vec::with_capacity(members.len());
|
||||||
|
|
||||||
// Padding structs will be named `_paddingN` where `N` is determined by this variable.
|
// Padding structs will be named `_paddingN` where `N` is determined by this variable.
|
||||||
@ -70,12 +53,12 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
|||||||
for (num, &member) in members.iter().enumerate() {
|
for (num, &member) in members.iter().enumerate() {
|
||||||
// Compute infos about the member.
|
// Compute infos about the member.
|
||||||
let (ty, rust_size, rust_align) = type_from_id(doc, member);
|
let (ty, rust_size, rust_align) = type_from_id(doc, member);
|
||||||
let member_name = ::member_name_from_id(doc, struct_id, num as u32);
|
let member_name = spirv_search::member_name_from_id(doc, struct_id, num as u32);
|
||||||
|
|
||||||
// Ignore the whole struct is a member is built in, which includes
|
// Ignore the whole struct is a member is built in, which includes
|
||||||
// `gl_Position` for example.
|
// `gl_Position` for example.
|
||||||
if is_builtin_member(doc, struct_id, num as u32) {
|
if is_builtin_member(doc, struct_id, num as u32) {
|
||||||
return (String::new(), None); // TODO: is this correct? shouldn't it return a correct struct but with a flag or something?
|
return (quote!{}, None); // TODO: is this correct? shouldn't it return a correct struct but with a flag or something?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finding offset of the current member, as requested by the SPIR-V code.
|
// Finding offset of the current member, as requested by the SPIR-V code.
|
||||||
@ -83,10 +66,10 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|i| {
|
.filter_map(|i| {
|
||||||
match *i {
|
match *i {
|
||||||
parse::Instruction::MemberDecorate {
|
Instruction::MemberDecorate {
|
||||||
target_id,
|
target_id,
|
||||||
member,
|
member,
|
||||||
decoration: enums::Decoration::DecorationOffset,
|
decoration: Decoration::DecorationOffset,
|
||||||
ref params,
|
ref params,
|
||||||
} if target_id == struct_id && member as usize == num => {
|
} if target_id == struct_id && member as usize == num => {
|
||||||
return Some(params[0]);
|
return Some(params[0]);
|
||||||
@ -102,7 +85,7 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
|||||||
// variables only. Ignoring these.
|
// variables only. Ignoring these.
|
||||||
let spirv_offset = match spirv_offset {
|
let spirv_offset = match spirv_offset {
|
||||||
Some(o) => o as usize,
|
Some(o) => o as usize,
|
||||||
None => return (String::new(), None), // TODO: shouldn't we return and let the caller ignore it instead?
|
None => return (quote!{}, None), // TODO: shouldn't we return and let the caller ignore it instead?
|
||||||
};
|
};
|
||||||
|
|
||||||
// We need to add a dummy field if necessary.
|
// We need to add a dummy field if necessary.
|
||||||
@ -124,10 +107,9 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
|||||||
let padding_num = next_padding_num;
|
let padding_num = next_padding_num;
|
||||||
next_padding_num += 1;
|
next_padding_num += 1;
|
||||||
rust_members.push(Member {
|
rust_members.push(Member {
|
||||||
name: format!("_dummy{}", padding_num),
|
name: Ident::new(&format!("_dummy{}", padding_num), Span::call_site()),
|
||||||
value: format!("[u8; {}]", diff),
|
ty: quote!{ [u8; #diff] },
|
||||||
offset: None,
|
});
|
||||||
});
|
|
||||||
*current_rust_offset += diff;
|
*current_rust_offset += diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,29 +122,28 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rust_members.push(Member {
|
rust_members.push(Member {
|
||||||
name: member_name.to_owned(),
|
name: Ident::new(&member_name, Span::call_site()),
|
||||||
value: ty,
|
ty,
|
||||||
offset: Some(spirv_offset),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try determine the total size of the struct in order to add padding at the end of the struct.
|
// Try determine the total size of the struct in order to add padding at the end of the struct.
|
||||||
let spirv_req_total_size = doc.instructions
|
let spirv_req_total_size = doc.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|i| match *i {
|
.filter_map(|i| match *i {
|
||||||
parse::Instruction::Decorate {
|
Instruction::Decorate {
|
||||||
target_id,
|
target_id,
|
||||||
decoration: enums::Decoration::DecorationArrayStride,
|
decoration: Decoration::DecorationArrayStride,
|
||||||
ref params,
|
ref params,
|
||||||
} => {
|
} => {
|
||||||
for inst in doc.instructions.iter() {
|
for inst in doc.instructions.iter() {
|
||||||
match *inst {
|
match *inst {
|
||||||
parse::Instruction::TypeArray {
|
Instruction::TypeArray {
|
||||||
result_id, type_id, ..
|
result_id, type_id, ..
|
||||||
} if result_id == target_id && type_id == struct_id => {
|
} if result_id == target_id && type_id == struct_id => {
|
||||||
return Some(params[0]);
|
return Some(params[0]);
|
||||||
},
|
},
|
||||||
parse::Instruction::TypeRuntimeArray { result_id, type_id }
|
Instruction::TypeRuntimeArray { result_id, type_id }
|
||||||
if result_id == target_id && type_id == struct_id => {
|
if result_id == target_id && type_id == struct_id => {
|
||||||
return Some(params[0]);
|
return Some(params[0]);
|
||||||
},
|
},
|
||||||
@ -186,58 +167,63 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String,
|
|||||||
let diff = req_size.checked_sub(cur_size as u32).unwrap();
|
let diff = req_size.checked_sub(cur_size as u32).unwrap();
|
||||||
if diff >= 1 {
|
if diff >= 1 {
|
||||||
rust_members.push(Member {
|
rust_members.push(Member {
|
||||||
name: format!("_dummy{}", next_padding_num),
|
name: Ident::new(&format!("_dummy{}", next_padding_num), Span::call_site()),
|
||||||
value: format!("[u8; {}]", diff),
|
ty: quote!{ [u8; {}] },
|
||||||
offset: None,
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can only implement Clone if there's no unsized member in the struct.
|
// We can only implement Clone if there's no unsized member in the struct.
|
||||||
let (impl_text, derive_text) = if current_rust_offset.is_some() {
|
let (clone_impl, copy_derive) = if current_rust_offset.is_some() {
|
||||||
let i = format!("\nimpl Clone for {name} {{\n fn clone(&self) -> Self {{\n \
|
let mut copies = vec!();
|
||||||
{name} {{\n{copies}\n }}\n }}\n}}\n",
|
for member in &rust_members {
|
||||||
name = name,
|
let name = &member.name;
|
||||||
copies = rust_members
|
copies.push(quote!{ #name: self.#name, });
|
||||||
.iter()
|
}
|
||||||
.map(Member::copy_text)
|
(
|
||||||
.collect::<Vec<_>>()
|
// Clone is implemented manually because members can be large arrays
|
||||||
.join(",\n"));
|
// that do not implement Clone, but do implement Copy
|
||||||
(i, "#[derive(Copy)]")
|
quote!{
|
||||||
|
impl Clone for #name {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
#name {
|
||||||
|
#( #copies )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
quote!{ #[derive(Copy)] }
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
("".to_owned(), "")
|
(quote!{}, quote!{})
|
||||||
};
|
};
|
||||||
|
|
||||||
let s =
|
let mut members = vec!();
|
||||||
format!("#[repr(C)]\n{derive_text}\n#[allow(non_snake_case)]\npub struct {name} \
|
for member in &rust_members {
|
||||||
{{\n{members}\n}} /* total_size: {t:?} */\n{impl_text}",
|
let name = &member.name;
|
||||||
name = name,
|
let ty = &member.ty;
|
||||||
members = rust_members
|
members.push(quote!(pub #name: #ty,));
|
||||||
.iter()
|
}
|
||||||
.map(Member::declaration_text)
|
|
||||||
.collect::<Vec<_>>()
|
let ast = quote! {
|
||||||
.join(",\n"),
|
#[repr(C)]
|
||||||
t = spirv_req_total_size,
|
#copy_derive
|
||||||
impl_text = impl_text,
|
#[allow(non_snake_case)]
|
||||||
derive_text = derive_text);
|
pub struct #name {
|
||||||
(s,
|
#( #members )*
|
||||||
spirv_req_total_size
|
}
|
||||||
.map(|sz| sz as usize)
|
#clone_impl
|
||||||
.or(current_rust_offset))
|
};
|
||||||
|
|
||||||
|
(ast, spirv_req_total_size.map(|sz| sz as usize).or(current_rust_offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if a `BuiltIn` decorator is applied on a struct member.
|
/// Returns true if a `BuiltIn` decorator is applied on a struct member.
|
||||||
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
|
fn is_builtin_member(doc: &Spirv, id: u32, member_id: u32) -> bool {
|
||||||
for instruction in &doc.instructions {
|
for instruction in &doc.instructions {
|
||||||
match *instruction {
|
match *instruction {
|
||||||
parse::Instruction::MemberDecorate {
|
Instruction::MemberDecorate { target_id, member, decoration: Decoration::DecorationBuiltIn, .. }
|
||||||
target_id,
|
if target_id == id && member == member_id => { return true }
|
||||||
member,
|
|
||||||
decoration: enums::Decoration::DecorationBuiltIn,
|
|
||||||
..
|
|
||||||
} if target_id == id && member == member_id => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,17 +234,13 @@ fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
|
|||||||
/// Returns the type name to put in the Rust struct, and its size and alignment.
|
/// Returns the type name to put in the Rust struct, and its size and alignment.
|
||||||
///
|
///
|
||||||
/// The size can be `None` if it's only known at runtime.
|
/// The size can be `None` if it's only known at runtime.
|
||||||
pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
|
pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>, usize) {
|
||||||
for instruction in doc.instructions.iter() {
|
for instruction in doc.instructions.iter() {
|
||||||
match instruction {
|
match instruction {
|
||||||
&parse::Instruction::TypeBool { result_id } if result_id == searched => {
|
&Instruction::TypeBool { result_id } if result_id == searched => {
|
||||||
panic!("Can't put booleans in structs")
|
panic!("Can't put booleans in structs")
|
||||||
},
|
}
|
||||||
&parse::Instruction::TypeInt {
|
&Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
|
||||||
result_id,
|
|
||||||
width,
|
|
||||||
signedness,
|
|
||||||
} if result_id == searched => {
|
|
||||||
match (width, signedness) {
|
match (width, signedness) {
|
||||||
(8, true) => {
|
(8, true) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -267,7 +249,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("i8".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{i8}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
(8, false) => {
|
(8, false) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -276,7 +258,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("u8".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{u8}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
(16, true) => {
|
(16, true) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -285,7 +267,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("i16".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{i16}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
(16, false) => {
|
(16, false) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -294,7 +276,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("u16".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{u16}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
(32, true) => {
|
(32, true) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -303,7 +285,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("i32".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{i32}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
(32, false) => {
|
(32, false) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -312,7 +294,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("u32".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{u32}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
(64, true) => {
|
(64, true) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -321,7 +303,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("i64".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{i64}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
(64, false) => {
|
(64, false) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -330,12 +312,12 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("u64".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{u64}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
_ => panic!("No Rust equivalent for an integer of width {}", width),
|
_ => panic!("No Rust equivalent for an integer of width {}", width),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
&parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
|
&Instruction::TypeFloat { result_id, width } if result_id == searched => {
|
||||||
match width {
|
match width {
|
||||||
32 => {
|
32 => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -344,7 +326,7 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("f32".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{f32}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
64 => {
|
64 => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -353,61 +335,58 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
after: u8,
|
after: u8,
|
||||||
}
|
}
|
||||||
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
|
||||||
return ("f64".to_owned(), Some(size), mem::align_of::<Foo>());
|
return (quote!{f64}, Some(size), mem::align_of::<Foo>());
|
||||||
},
|
},
|
||||||
_ => panic!("No Rust equivalent for a floating-point of width {}", width),
|
_ => panic!("No Rust equivalent for a floating-point of width {}", width),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
&parse::Instruction::TypeVector {
|
&Instruction::TypeVector {
|
||||||
result_id,
|
result_id,
|
||||||
component_id,
|
component_id,
|
||||||
count,
|
count,
|
||||||
} if result_id == searched => {
|
} if result_id == searched => {
|
||||||
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
||||||
let (t, t_size, t_align) = type_from_id(doc, component_id);
|
let (ty, t_size, t_align) = type_from_id(doc, component_id);
|
||||||
return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
|
let array_length = count as usize;
|
||||||
},
|
let size = t_size.map(|s| s * count as usize);
|
||||||
&parse::Instruction::TypeMatrix {
|
return (quote!{ [#ty; #array_length] }, size, t_align);
|
||||||
|
}
|
||||||
|
&Instruction::TypeMatrix {
|
||||||
result_id,
|
result_id,
|
||||||
column_type_id,
|
column_type_id,
|
||||||
column_count,
|
column_count,
|
||||||
} if result_id == searched => {
|
} if result_id == searched => {
|
||||||
// FIXME: row-major or column-major
|
// FIXME: row-major or column-major
|
||||||
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
||||||
let (t, t_size, t_align) = type_from_id(doc, column_type_id);
|
let (ty, t_size, t_align) = type_from_id(doc, column_type_id);
|
||||||
return (format!("[{}; {}]", t, column_count),
|
let array_length = column_count as usize;
|
||||||
t_size.map(|s| s * column_count as usize),
|
let size = t_size.map(|s| s * column_count as usize);
|
||||||
t_align);
|
return (quote!{ [#ty; #array_length] }, size, t_align);
|
||||||
},
|
}
|
||||||
&parse::Instruction::TypeArray {
|
&Instruction::TypeArray {
|
||||||
result_id,
|
result_id,
|
||||||
type_id,
|
type_id,
|
||||||
length_id,
|
length_id,
|
||||||
} if result_id == searched => {
|
} if result_id == searched => {
|
||||||
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
||||||
let (t, t_size, t_align) = type_from_id(doc, type_id);
|
let (ty, t_size, t_align) = type_from_id(doc, type_id);
|
||||||
let t_size = t_size.expect("array components must be sized");
|
let t_size = t_size.expect("array components must be sized");
|
||||||
let len = doc.instructions
|
let len = doc.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|e| match e {
|
.filter_map(|e| match e {
|
||||||
&parse::Instruction::Constant {
|
&Instruction::Constant { result_id, ref data, .. }
|
||||||
result_id,
|
if result_id == length_id => Some(data.clone()),
|
||||||
ref data,
|
_ => None,
|
||||||
..
|
})
|
||||||
} if result_id == length_id => Some(data.clone()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.next()
|
.next()
|
||||||
.expect("failed to find array length");
|
.expect("failed to find array length");
|
||||||
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
|
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
|
||||||
let stride = doc.instructions.iter().filter_map(|e| match e {
|
let stride = doc.instructions.iter().filter_map(|e| match e {
|
||||||
parse::Instruction::Decorate{
|
Instruction::Decorate {
|
||||||
target_id,
|
target_id,
|
||||||
decoration: enums::Decoration::DecorationArrayStride,
|
decoration: Decoration::DecorationArrayStride,
|
||||||
ref params,
|
ref params,
|
||||||
} if *target_id == searched => {
|
} if *target_id == searched => Some(params[0]),
|
||||||
Some(params[0])
|
|
||||||
},
|
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.next().expect("failed to find ArrayStride decoration");
|
.next().expect("failed to find ArrayStride decoration");
|
||||||
@ -417,27 +396,30 @@ pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>
|
|||||||
the array element in a struct or rounding up the size of a vector or matrix \
|
the array element in a struct or rounding up the size of a vector or matrix \
|
||||||
(e.g. increase a vec3 to a vec4)")
|
(e.g. increase a vec3 to a vec4)")
|
||||||
}
|
}
|
||||||
return (format!("[{}; {}]", t, len), Some(t_size * len as usize), t_align);
|
let array_length = len as usize;
|
||||||
},
|
let size = Some(t_size * len as usize);
|
||||||
&parse::Instruction::TypeRuntimeArray { result_id, type_id }
|
return (quote!{ [#ty; #array_length] }, size, t_align);
|
||||||
|
}
|
||||||
|
&Instruction::TypeRuntimeArray { result_id, type_id }
|
||||||
if result_id == searched => {
|
if result_id == searched => {
|
||||||
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
|
||||||
let (t, _, t_align) = type_from_id(doc, type_id);
|
let (ty, _, t_align) = type_from_id(doc, type_id);
|
||||||
return (format!("[{}]", t), None, t_align);
|
return (quote!{ [#ty] }, None, t_align);
|
||||||
},
|
}
|
||||||
&parse::Instruction::TypeStruct {
|
&Instruction::TypeStruct {
|
||||||
result_id,
|
result_id,
|
||||||
ref member_types,
|
ref member_types,
|
||||||
} if result_id == searched => {
|
} if result_id == searched => {
|
||||||
// TODO: take the Offset member decorate into account?
|
// TODO: take the Offset member decorate into account?
|
||||||
let name = ::name_from_id(doc, result_id);
|
let name = Ident::new(&spirv_search::name_from_id(doc, result_id), Span::call_site());
|
||||||
|
let ty = quote!{ #name };
|
||||||
let (_, size) = write_struct(doc, result_id, member_types);
|
let (_, size) = write_struct(doc, result_id, member_types);
|
||||||
let align = member_types
|
let align = member_types
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&t| type_from_id(doc, t).2)
|
.map(|&t| type_from_id(doc, t).2)
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(1);
|
.unwrap_or(1);
|
||||||
return (name, size, align);
|
return (ty, size, align);
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user